From 43b01d3a59c992b3f7fcef3ba87e2cbc42fea4a7 Mon Sep 17 00:00:00 2001 From: Munish Dabra <100611213+awsdabra@users.noreply.github.com> Date: Tue, 15 Aug 2023 03:59:50 +0000 Subject: [PATCH] Added code generation module and examples --- .../00_code_generatation_w_bedrock.ipynb | 995 +++++++++++++++ .../01_sql_query_generate_w_bedrock.ipynb | 1025 +++++++++++++++ .../02_code_interpret_w_langchain.ipynb | 967 ++++++++++++++ .../03_code_translate_w_langchain.ipynb | 1132 +++++++++++++++++ 06_CodeGeneration/README.md | 37 + .../images/bedrock-code-gen-langchain.png | Bin 0 -> 71989 bytes 06_CodeGeneration/images/bedrock-code-gen.png | Bin 0 -> 56538 bytes 06_CodeGeneration/sales.csv | 26 + README.md | 11 + 9 files changed, 4193 insertions(+) create mode 100644 06_CodeGeneration/00_code_generatation_w_bedrock.ipynb create mode 100644 06_CodeGeneration/01_sql_query_generate_w_bedrock.ipynb create mode 100644 06_CodeGeneration/02_code_interpret_w_langchain.ipynb create mode 100644 06_CodeGeneration/03_code_translate_w_langchain.ipynb create mode 100644 06_CodeGeneration/README.md create mode 100644 06_CodeGeneration/images/bedrock-code-gen-langchain.png create mode 100644 06_CodeGeneration/images/bedrock-code-gen.png create mode 100644 06_CodeGeneration/sales.csv diff --git a/06_CodeGeneration/00_code_generatation_w_bedrock.ipynb b/06_CodeGeneration/00_code_generatation_w_bedrock.ipynb new file mode 100644 index 00000000..7f8cd2b5 --- /dev/null +++ b/06_CodeGeneration/00_code_generatation_w_bedrock.ipynb @@ -0,0 +1,995 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "dc40c48b-0c95-4757-a067-563cfccd51a5", + "metadata": { + "tags": [] + }, + "source": [ + "# Invoke Bedrock model for code generation\n", + "\n", + "> *This notebook should work well with the **`Data Science 3.0`** kernel in SageMaker Studio*" + ] + }, + { + "cell_type": "markdown", + "id": "c9a413e2-3c34-4073-9000-d8556537bb6a", + "metadata": {}, + "source": [ + "## Introduction\n", + "\n", + "In this notebook we show you how to use a LLM to generate code based on the text prompt.\n", + "\n", + "We will use Bedrock's Claude v2 using the Boto3 API. \n", + "\n", + "The prompt used in this example is called a zero-shot prompt because we are not providing any examples of text other than the prompt.\n", + "\n", + "**Note:** *This notebook can be run within or outside of AWS environment.*\n", + "\n", + "#### Context\n", + "To demonstrate the code generation capability of Amazon Bedrock, we will explore the use of Boto3 client to communicate with Amazon Bedrock API. We will demonstrate different configurations available as well as how simple input can lead to desired outputs.\n", + "\n", + "#### Pattern\n", + "We will simply provide the Amazon Bedrock API with an input consisting of a task, an instruction and an input for the model under the hood to generate an output without providing any additional example. The purpose here is to demonstrate how the powerful LLMs easily understand the task at hand and generate compelling outputs.\n", + "\n", + "![](./images/bedrock-code-gen.png)\n", + "\n", + "#### Use case\n", + "To demonstrate the generation capability of models in Amazon Bedrock, let's take the use case of code generation.\n", + "\n", + "#### Persona\n", + "\n", + "You are Moe, a Data Analyst, at AnyCompany. The company wants to understand its sales performance for different products for different products over the past year. You have been provided a dataset named sales.csv. The dataset contains the following columns:\n", + "\n", + "- Date (YYYY-MM-DD) format\n", + "- Product_ID (unique identifer for each product)\n", + "- Price (price at which each product was sold)\n", + "\n", + "#### Implementation\n", + "To fulfill this use case, in this notebook we will show how to generate code for a given prompt.We will use the Anthropic Claude v2 using the Amazon Bedrock API with Boto3 client. " + ] + }, + { + "cell_type": "markdown", + "id": "64baae27-2660-4a1e-b2e5-3de49d069362", + "metadata": {}, + "source": [ + "## Setup\n", + "\n", + "Before running the rest of this notebook, you'll need to run the cells below to (ensure necessary libraries are installed and) connect to Bedrock.\n", + "\n", + "For more details on how the setup works and ⚠️ **whether you might need to make any changes**, refer to the [Bedrock boto3 setup notebook](../00_Intro/bedrock_boto3_setup.ipynb) notebook." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "38b791ad-e6c5-4da5-96af-5c356a36e19d", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Make sure you ran `download-dependencies.sh` from the root of the repository first!\n", + "%pip install --no-build-isolation --force-reinstall \\\n", + " ../dependencies/awscli-*-py3-none-any.whl \\\n", + " ../dependencies/boto3-*-py3-none-any.whl \\\n", + " ../dependencies/botocore-*-py3-none-any.whl\n", + "\n", + "%pip install --quiet langchain==0.0.249" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7ea26558", + "metadata": {}, + "outputs": [], + "source": [ + "# Optional - To execute the generated code in this notebook\n", + "%pip install matplotlib" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "776fd083", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "import json\n", + "import os\n", + "import sys\n", + "\n", + "import boto3\n", + "\n", + "module_path = \"..\"\n", + "sys.path.append(os.path.abspath(module_path))\n", + "from utils import bedrock, print_ww\n", + "\n", + "\n", + "# ---- ⚠️ Un-comment and edit the below lines as needed for your AWS setup ⚠️ ----\n", + "\n", + "os.environ[\"AWS_DEFAULT_REGION\"] = \"us-east-1\" # E.g. \"us-east-1\"\n", + "os.environ[\"AWS_PROFILE\"] = \"fine-tuning-bedrock\"\n", + "# os.environ[\"BEDROCK_ASSUME_ROLE\"] = \"\" # E.g. \"arn:aws:...\"\n", + "# os.environ[\"BEDROCK_ENDPOINT_URL\"] = \"\" # E.g. \"https://...\"\n", + "\n", + "\n", + "boto3_bedrock = bedrock.get_bedrock_client(\n", + " assumed_role=os.environ.get(\"BEDROCK_ASSUME_ROLE\", None),\n", + " endpoint_url=os.environ.get(\"BEDROCK_ENDPOINT_URL\", None),\n", + " region=os.environ.get(\"AWS_DEFAULT_REGION\", None),\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "4f634211-3de1-4390-8c3f-367af5554c39", + "metadata": {}, + "source": [ + "## Code Generation\n", + "\n", + "Following on the use case explained above, let's prepare an input for the Amazon Bedrock service to generate python program for our use-case." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "45ee2bae-6415-4dba-af98-a19028305c98", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Create the prompt\n", + "# Analyzing sales with a Python Program\n", + "\n", + "prompt_data = \"\"\"\n", + "Command: Human: You have a CSV, sales.csv, with columns:\n", + "- date (YYYY-MM-DD)\n", + "- product_id\n", + "- price\n", + "- units_sold\n", + "\n", + "Wrte a python program to load the data and determine \n", + "\n", + "- Total revenue for the year\n", + "- The product with the highest revenue\n", + "- The date with the highest revenue\n", + "- Visualize monthly sales using a bar chart\n", + "\n", + "Assistant:\n", + "\"\"\"" + ] + }, + { + "cell_type": "markdown", + "id": "cc9784e5-5e9d-472d-8ef1-34108ee4968b", + "metadata": {}, + "source": [ + "Let's start by using the Anthropic Claude V2 model." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "8af670eb-ad02-40df-a19c-3ed835fac8d9", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Claude - Body Syntex\n", + "body = json.dumps({\n", + " \"prompt\": prompt_data,\n", + " \"max_tokens_to_sample\":4096,\n", + " \"temperature\":0.5,\n", + " \"top_k\":250,\n", + " \"top_p\":0.5,\n", + " \"stop_sequences\": [\"\\n\\nHuman:\"]\n", + " }) " + ] + }, + { + "cell_type": "markdown", + "id": "c4ca6751", + "metadata": {}, + "source": [ + "The Amazon Bedrock API provides you with an API `invoke_model` which accepts the following:\n", + "- `modelId`: This is the model ARN for the various foundation models available under Amazon Bedrock\n", + "- `accept`: The type of input request\n", + "- `contentType`: The content type of the output\n", + "- `body`: A json string consisting of the prompt and the configurations\n", + "\n", + "Available text generation models under Amazon Bedrock have the following IDs:\n", + "- `amazon.titan-tg1-large`\n", + "- `amazon.titan-e1t-medium`\n", + "- `ai21.j2-grande-instruct`\n", + "- `ai21.j2-jumbo-instruct`\n", + "- `ai21.j2-mid`\n", + "- `ai21.j2-ultra`\n", + "- `anthropic.claude-instant-v1`\n", + "- `anthropic.claude-v1`\n", + "- `anthropic.claude-v2`" + ] + }, + { + "cell_type": "markdown", + "id": "088cf6bf-dd73-4710-a0cc-6c11d220c431", + "metadata": {}, + "source": [ + "#### Invoke the Anthropic Claude v2 model" + ] + }, + { + "cell_type": "markdown", + "id": "379498f2", + "metadata": {}, + "source": [ + "First, we explore how the model generates an output based on the prompt created earlier.\n", + "\n", + "##### Complete Output Generation" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "016a118a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Here is a Python program to analyze the sales CSV file as described:\n", + "\n", + "```python\n", + "import csv\n", + "from collections import defaultdict\n", + "import matplotlib.pyplot as plt\n", + "\n", + "revenue_by_month = defaultdict(int)\n", + "\n", + "with open('sales.csv', 'r') as f:\n", + " reader = csv.DictReader(f)\n", + " total_revenue = 0\n", + " max_revenue_product = None\n", + " max_revenue = 0\n", + " max_revenue_date = None\n", + "\n", + " for row in reader:\n", + " revenue = float(row['price']) * int(row['units_sold'])\n", + " total_revenue += revenue\n", + "\n", + " date = row['date']\n", + " month = date.split('-')[1]\n", + " revenue_by_month[month] += revenue\n", + "\n", + " if revenue > max_revenue:\n", + " max_revenue = revenue\n", + " max_revenue_product = row['product_id']\n", + " max_revenue_date = date\n", + "\n", + "print('Total revenue:', total_revenue)\n", + "print('Product with max revenue:', max_revenue_product)\n", + "print('Date with max revenue:', max_revenue_date)\n", + "\n", + "plt.bar(revenue_by_month.keys(), revenue_by_month.values())\n", + "plt.xlabel('Month')\n", + "plt.ylabel('Revenue')\n", + "plt.title('Revenue by Month')\n", + "plt.show()\n", + "```\n", + "\n", + "This loads the CSV data, calculates the total revenue, finds the product and date with max revenue,\n", + "and visualizes the revenue per month in a bar chart. The defaultdict is used to easily accumulate\n", + "values by month.\n" + ] + } + ], + "source": [ + "modelId = 'anthropic.claude-v2' # change this to use a different version from the model provider\n", + "accept = 'application/json'\n", + "contentType = 'application/json'\n", + "\n", + "response = boto3_bedrock.invoke_model(body=body, modelId=modelId, accept=accept, contentType=contentType)\n", + "response_body = json.loads(response.get('body').read())\n", + "\n", + "print_ww(response_body.get('completion'))" + ] + }, + { + "cell_type": "markdown", + "id": "ddddd1ec", + "metadata": {}, + "source": [ + "#### (Optional) Execute the Bedrock generated code for validation. Go to text editor to copy the generated code as printed output can be trucncated. Replce the code in below cell." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "395fad3b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Total revenue: 35490.0\n", + "Product with max revenue: P003\n", + "Date with max revenue: 2023-04-23\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAk0AAAHHCAYAAACiOWx7AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAA9hAAAPYQGoP6dpAAA/30lEQVR4nO3df3zN9f//8fuZ2Y+wzbDNNCwKiyjeWAm97WvYu1opv0Zkkd5bPyg/9q786gdWy4+Spd6ZincovEV+LKq9Mb+GRCi9J8K2vGc7ITPb6/uHz16XTvPjZY1zNrfr5XIuF+f1fJznebyeabt7vV7ndWyGYRgCAADAJbk5uwEAAICKgNAEAABgAaEJAADAAkITAACABYQmAAAACwhNAAAAFhCaAAAALCA0AQAAWEBoAgAAsIDQBAAWde7cWc2bN3d2Gy5h0KBBql69urPbAK4pQhNwHUpJSZHNZjMf7u7uqlevngYNGqQjR444u73rXsl/l8cee+yC488//7xZc/z48avWx+nTpzV+/Hh99dVXV+09gIrE3dkNAHCeiRMnKjQ0VGfOnNGmTZuUkpKi9evXa/fu3fLy8nJ2e9c1Ly8vffrpp3r77bfl4eHhMPavf/1LXl5eOnPmzFXt4fTp05owYYKk80fZgOsdR5qA61j37t3Vv39/PfbYY3rvvff03HPP6ccff9SyZcuc3dp1r1u3brLb7Vq5cqXD9o0bNyozM1NRUVFO6gy4fhGaAJjuvvtuSdKPP/7osH3fvn166KGH5O/vLy8vL7Vp08YhWG3btk02m01z584tNefq1atls9m0fPlyc9uRI0c0ePBgBQYGytPTU7feeqvef/99h9d99dVXstlsWrhwoV555RXdeOON8vLyUpcuXXTgwAGH2oYNG2rQoEGl3rtz586ljpAUFBRo3Lhxaty4sTw9PRUSEqJRo0apoKDA0hpJUkZGhu688055e3srNDRUycnJ5tjJkydVrVo1Pf3006Ve9/PPP6tKlSqaNGnSZd+jXr166tixo+bPn++wfd68eWrRosVFr61atGiRWrduLW9vb9WuXVv9+/cvdcq15HqkI0eOKDo6WtWrV1edOnX03HPPqaioSJJ08OBB1alTR5I0YcIE83Tg+PHjHea61BxAZUNoAmA6ePCgJKlmzZrmtj179qh9+/bau3evxowZo6SkJFWrVk3R0dFasmSJJKlNmza66aabtHDhwlJzLliwQDVr1lRkZKQkKTs7W+3bt9cXX3yh+Ph4TZ8+XY0bN1ZsbKymTZtW6vWTJ0/WkiVL9NxzzykhIUGbNm1STExMmfavuLhY9913n15//XXde++9evPNNxUdHa2pU6eqd+/eluY4ceKEevToodatWysxMVE33nijnnjiCTP0Va9eXQ888IAWLFhQKjz861//kmEYlvvv16+fPvvsM508eVKSdO7cOS1atEj9+vW7YH1KSop69eplBrMhQ4Zo8eLF6tChg/Ly8hxqi4qKFBkZqVq1aun1119Xp06dlJSUpNmzZ0uS6tSpo1mzZkmSHnjgAX344Yf68MMP9eCDD1qeA6h0DADXnTlz5hiSjC+++ML45ZdfjMOHDxuffPKJUadOHcPT09M4fPiwWdulSxejRYsWxpkzZ8xtxcXFxp133mncfPPN5raEhASjatWqRm5urrmtoKDA8PPzMwYPHmxui42NNerWrWscP37coac+ffoYvr6+xunTpw3DMIwvv/zSkGQ0a9bMKCgoMOumT59uSDK+/fZbc1uDBg2MgQMHltrPTp06GZ06dTKff/jhh4abm5vxn//8x6EuOTnZkGRs2LDhkuvWqVMnQ5KRlJTksI+tWrUyAgICjLNnzxqGYRirV682JBkrV650eP1tt93m0M/FSDLi4uKM3Nxcw8PDw/jwww8NwzCMFStWGDabzTh48KAxbtw4Q5Lxyy+/GIZhGGfPnjUCAgKM5s2bG7/99ps51/Llyw1JxtixY81tAwcONCQZEydOdHjf22+/3WjdurX5/JdffjEkGePGjSvVo9U5gMqEI03AdSwiIkJ16tRRSEiIHnroIVWrVk3Lli3TjTfeKEnKzc3VunXr1KtXL/366686fvy4jh8/rv/973+KjIzUDz/8YJ766d27twoLC7V48WJz/jVr1igvL888imMYhj799FPde++9MgzDnO/48eOKjIxUfn6+tm/f7tDjo48+6nAhdMkpxP/+979XvL+LFi1Ss2bN1LRpU4f3/utf/ypJ+vLLLy87h7u7ux5//HHzuYeHhx5//HHl5OQoIyPDXNfg4GDNmzfPrNu9e7d27dql/v37W+63Zs2a6tatm/71r39JkubPn68777xTDRo0KFW7bds25eTk6O9//7vDRfxRUVFq2rSpVqxYUeo1w4YNc3h+9913X/G6lsccQEXBp+eA69jMmTN1yy23KD8/X++//77S0tLk6elpjh84cECGYejFF1/Uiy++eME5cnJyVK9ePbVs2VJNmzbVggULFBsbK+n8qbnatWuboeSXX35RXl6eZs+efdFTODk5OQ7P69ev7/C85NThiRMnrnh/f/jhB+3du9e8Vudy730hwcHBqlatmsO2W265RdL505vt27eXm5ubYmJiNGvWLJ0+fVo33HCD5s2bJy8vLz388MNX1HO/fv00YMAAHTp0SEuXLlViYuIF63766SdJUpMmTUqNNW3aVOvXr3fY5uXlVWodataseUXrWh5zABUJoQm4jrVt21Zt2rSRJEVHR6tDhw7q16+f9u/fr+rVq6u4uFiS9Nxzz5nXJP1R48aNzT/37t1br7zyio4fP64aNWpo2bJl6tu3r9zdz/+oKZmvf//+Gjhw4AXnu+222xyeV6lS5YJ1hmGYf7bZbBesKSoqcnh9cXGxWrRooTfeeOOC9SEhIRfcXhaPPPKIXnvtNS1dulR9+/bV/Pnz9be//U2+vr5XNM99990nT09PDRw4UAUFBerVq1e59Hexdb3WcwAVCaEJgCSZFw/fc889euuttzRmzBjddNNNkqSqVasqIiLisnP07t1bEyZM0KeffqrAwEDZ7Xb16dPHHK9Tp45q1KihoqIiS/NZVbNmzVIXOkvnj76U7IMkNWrUSN988426dOly0aB1OUePHtWpU6ccjjZ9//33ks5/iq9E8+bNdfvtt2vevHm68cYbdejQIb355ptX/H7e3t6Kjo7WRx99pO7du6t27doXrCs5Zbd//37zyF6J/fv3X/CU3uWUdY2AyoprmgCYOnfurLZt22ratGk6c+aMAgIC1LlzZ73zzjs6duxYqfpffvnF4XmzZs3UokULLViwQAsWLFDdunXVsWNHc7xKlSrq2bOnPv30U+3evfuy81nVqFEjbdq0SWfPnjW3LV++XIcPH3ao69Wrl44cOaJ333231By//fabTp06ddn3OnfunN555x3z+dmzZ/XOO++oTp06at26tUPtgAEDtGbNGk2bNk21atVS9+7dr3TXJJ0/0jdu3LiLniKVzn+CMSAgQMnJyQ63T1i5cqX27t1bpvs63XDDDZJ0wUAKXI840gTAwciRI/Xwww8rJSVFw4YN08yZM9WhQwe1aNFC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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Sample Generated Python Code ( Generated with Amazon Bedrock in previous step)\n", + "\n", + "import csv\n", + "from collections import defaultdict\n", + "import matplotlib.pyplot as plt\n", + " \n", + "revenue_by_month = defaultdict(int)\n", + "\n", + "with open('sales.csv', 'r') as f:\n", + " reader = csv.DictReader(f)\n", + " total_revenue = 0\n", + " max_revenue_product = None\n", + " max_revenue = 0\n", + " max_revenue_date = None\n", + "\n", + " for row in reader:\n", + " revenue = float(row['price']) * int(row['units_sold'])\n", + " total_revenue += revenue\n", + "\n", + " date = row['date']\n", + " month = date.split('-')[1]\n", + " revenue_by_month[month] += revenue\n", + "\n", + " if revenue > max_revenue:\n", + " max_revenue = revenue\n", + " max_revenue_product = row['product_id']\n", + " max_revenue_date = date\n", + "\n", + "print('Total revenue:', total_revenue)\n", + "print('Product with max revenue:', max_revenue_product)\n", + "print('Date with max revenue:', max_revenue_date)\n", + "'\n", + "# Plot 'Revenue by Month'\n", + "plt.bar(revenue_by_month.keys(), revenue_by_month.values())\n", + "plt.xlabel('Month')\n", + "plt.ylabel('Revenue')\n", + "plt.title('Revenue by Month')\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "64b08b3b", + "metadata": {}, + "source": [ + "## Conclusion\n", + "You have now experimented with using `boto3` SDK which provides a vanilla exposure to Amazon Bedrock API. Using this API you generate a python program to analyze and visualize given sales data'\n", + "\n", + "### Take aways\n", + "- Adapt this notebook to experiment with different models available through Amazon Bedrock such as Amazon Titan and AI21 Labs Jurassic models.\n", + "- Change the prompts to your specific usecase and evaluate the output of different models.\n", + "- Play with the token length to understand the latency and responsiveness of the service.\n", + "- Apply different prompt engineering principles to get better outputs.\n", + "\n", + "## Thank You" + ] + } + ], + "metadata": { + "availableInstances": [ + { + "_defaultOrder": 0, + "_isFastLaunch": true, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 4, + "name": "ml.t3.medium", + "vcpuNum": 2 + }, + { + "_defaultOrder": 1, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 8, + "name": "ml.t3.large", + "vcpuNum": 2 + }, + { + "_defaultOrder": 2, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 16, + "name": "ml.t3.xlarge", + "vcpuNum": 4 + }, + { + "_defaultOrder": 3, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 32, + "name": "ml.t3.2xlarge", + "vcpuNum": 8 + }, + { + "_defaultOrder": 4, + "_isFastLaunch": true, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 8, + "name": "ml.m5.large", + "vcpuNum": 2 + }, + { + "_defaultOrder": 5, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 16, + "name": "ml.m5.xlarge", + "vcpuNum": 4 + }, + { + "_defaultOrder": 6, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 32, + "name": "ml.m5.2xlarge", + "vcpuNum": 8 + }, + { + "_defaultOrder": 7, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 64, + "name": "ml.m5.4xlarge", + "vcpuNum": 16 + }, + { + "_defaultOrder": 8, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 128, + "name": "ml.m5.8xlarge", + "vcpuNum": 32 + }, + { + "_defaultOrder": 9, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 192, + "name": "ml.m5.12xlarge", + "vcpuNum": 48 + }, + { + "_defaultOrder": 10, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 256, + "name": "ml.m5.16xlarge", + "vcpuNum": 64 + }, + { + "_defaultOrder": 11, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 384, + "name": "ml.m5.24xlarge", + "vcpuNum": 96 + }, + { + "_defaultOrder": 12, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 8, + "name": "ml.m5d.large", + "vcpuNum": 2 + }, + { + "_defaultOrder": 13, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 16, + "name": "ml.m5d.xlarge", + "vcpuNum": 4 + }, + { + "_defaultOrder": 14, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 32, + "name": "ml.m5d.2xlarge", + "vcpuNum": 8 + }, + { + "_defaultOrder": 15, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 64, + "name": "ml.m5d.4xlarge", + "vcpuNum": 16 + }, + { + "_defaultOrder": 16, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 128, + "name": "ml.m5d.8xlarge", + "vcpuNum": 32 + }, + { + "_defaultOrder": 17, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 192, + "name": "ml.m5d.12xlarge", + "vcpuNum": 48 + }, + { + "_defaultOrder": 18, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 256, + "name": "ml.m5d.16xlarge", + "vcpuNum": 64 + }, + { + "_defaultOrder": 19, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 384, + "name": "ml.m5d.24xlarge", + "vcpuNum": 96 + }, + { + "_defaultOrder": 20, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": true, + "memoryGiB": 0, + "name": "ml.geospatial.interactive", + "supportedImageNames": [ + "sagemaker-geospatial-v1-0" + ], + "vcpuNum": 0 + }, + { + "_defaultOrder": 21, + "_isFastLaunch": true, + "category": "Compute optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 4, + "name": "ml.c5.large", + "vcpuNum": 2 + }, + { + "_defaultOrder": 22, + "_isFastLaunch": false, + "category": "Compute optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 8, + "name": "ml.c5.xlarge", + "vcpuNum": 4 + }, + { + "_defaultOrder": 23, + "_isFastLaunch": false, + "category": "Compute optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 16, + "name": "ml.c5.2xlarge", + "vcpuNum": 8 + }, + { + "_defaultOrder": 24, + "_isFastLaunch": false, + "category": "Compute optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 32, + "name": "ml.c5.4xlarge", + "vcpuNum": 16 + }, + { + "_defaultOrder": 25, + "_isFastLaunch": false, + "category": "Compute optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 72, + "name": "ml.c5.9xlarge", + "vcpuNum": 36 + }, + { + "_defaultOrder": 26, + "_isFastLaunch": false, + "category": "Compute optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 96, + "name": "ml.c5.12xlarge", + "vcpuNum": 48 + }, + { + "_defaultOrder": 27, + "_isFastLaunch": false, + "category": "Compute optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 144, + "name": "ml.c5.18xlarge", + "vcpuNum": 72 + }, + { + "_defaultOrder": 28, + "_isFastLaunch": false, + "category": "Compute optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 192, + "name": "ml.c5.24xlarge", + "vcpuNum": 96 + }, + { + "_defaultOrder": 29, + "_isFastLaunch": true, + "category": "Accelerated computing", + "gpuNum": 1, + "hideHardwareSpecs": false, + "memoryGiB": 16, + "name": "ml.g4dn.xlarge", + "vcpuNum": 4 + }, + { + "_defaultOrder": 30, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 1, + "hideHardwareSpecs": false, + "memoryGiB": 32, + "name": "ml.g4dn.2xlarge", + "vcpuNum": 8 + }, + { + "_defaultOrder": 31, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 1, + "hideHardwareSpecs": false, + "memoryGiB": 64, + "name": "ml.g4dn.4xlarge", + "vcpuNum": 16 + }, + { + "_defaultOrder": 32, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 1, + "hideHardwareSpecs": false, + "memoryGiB": 128, + "name": "ml.g4dn.8xlarge", + "vcpuNum": 32 + }, + { + "_defaultOrder": 33, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 4, + "hideHardwareSpecs": false, + "memoryGiB": 192, + "name": "ml.g4dn.12xlarge", + "vcpuNum": 48 + }, + { + "_defaultOrder": 34, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 1, + "hideHardwareSpecs": false, + "memoryGiB": 256, + "name": "ml.g4dn.16xlarge", + "vcpuNum": 64 + }, + { + "_defaultOrder": 35, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 1, + "hideHardwareSpecs": false, + "memoryGiB": 61, + "name": "ml.p3.2xlarge", + "vcpuNum": 8 + }, + { + "_defaultOrder": 36, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 4, + "hideHardwareSpecs": false, + "memoryGiB": 244, + "name": "ml.p3.8xlarge", + "vcpuNum": 32 + }, + { + "_defaultOrder": 37, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 8, + "hideHardwareSpecs": false, + "memoryGiB": 488, + "name": "ml.p3.16xlarge", + "vcpuNum": 64 + }, + { + "_defaultOrder": 38, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 8, + "hideHardwareSpecs": false, + "memoryGiB": 768, + "name": "ml.p3dn.24xlarge", + "vcpuNum": 96 + }, + { + "_defaultOrder": 39, + "_isFastLaunch": false, + "category": "Memory Optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 16, + "name": "ml.r5.large", + "vcpuNum": 2 + }, + { + "_defaultOrder": 40, + "_isFastLaunch": false, + "category": "Memory Optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 32, + "name": "ml.r5.xlarge", + "vcpuNum": 4 + }, + { + "_defaultOrder": 41, + "_isFastLaunch": false, + "category": "Memory Optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 64, + "name": "ml.r5.2xlarge", + "vcpuNum": 8 + }, + { + "_defaultOrder": 42, + "_isFastLaunch": false, + "category": "Memory Optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 128, + "name": "ml.r5.4xlarge", + "vcpuNum": 16 + }, + { + "_defaultOrder": 43, + "_isFastLaunch": false, + "category": "Memory Optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 256, + "name": "ml.r5.8xlarge", + "vcpuNum": 32 + }, + { + "_defaultOrder": 44, + "_isFastLaunch": false, + "category": "Memory Optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 384, + "name": "ml.r5.12xlarge", + "vcpuNum": 48 + }, + { + "_defaultOrder": 45, + "_isFastLaunch": false, + "category": "Memory Optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 512, + "name": "ml.r5.16xlarge", + "vcpuNum": 64 + }, + { + "_defaultOrder": 46, + "_isFastLaunch": false, + "category": "Memory Optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 768, + "name": "ml.r5.24xlarge", + "vcpuNum": 96 + }, + { + "_defaultOrder": 47, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 1, + "hideHardwareSpecs": false, + "memoryGiB": 16, + "name": "ml.g5.xlarge", + "vcpuNum": 4 + }, + { + "_defaultOrder": 48, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 1, + "hideHardwareSpecs": false, + "memoryGiB": 32, + "name": "ml.g5.2xlarge", + "vcpuNum": 8 + }, + { + "_defaultOrder": 49, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 1, + "hideHardwareSpecs": false, + "memoryGiB": 64, + "name": "ml.g5.4xlarge", + "vcpuNum": 16 + }, + { + "_defaultOrder": 50, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 1, + "hideHardwareSpecs": false, + "memoryGiB": 128, + "name": "ml.g5.8xlarge", + "vcpuNum": 32 + }, + { + "_defaultOrder": 51, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 1, + "hideHardwareSpecs": false, + "memoryGiB": 256, + "name": "ml.g5.16xlarge", + "vcpuNum": 64 + }, + { + "_defaultOrder": 52, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 4, + "hideHardwareSpecs": false, + "memoryGiB": 192, + "name": "ml.g5.12xlarge", + "vcpuNum": 48 + }, + { + "_defaultOrder": 53, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 4, + "hideHardwareSpecs": false, + "memoryGiB": 384, + "name": "ml.g5.24xlarge", + "vcpuNum": 96 + }, + { + "_defaultOrder": 54, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 8, + "hideHardwareSpecs": false, + "memoryGiB": 768, + "name": "ml.g5.48xlarge", + "vcpuNum": 192 + }, + { + "_defaultOrder": 55, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 8, + "hideHardwareSpecs": false, + "memoryGiB": 1152, + "name": "ml.p4d.24xlarge", + "vcpuNum": 96 + }, + { + "_defaultOrder": 56, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 8, + "hideHardwareSpecs": false, + "memoryGiB": 1152, + "name": "ml.p4de.24xlarge", + "vcpuNum": 96 + } + ], + "instance_type": "ml.t3.medium", + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.8" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/06_CodeGeneration/01_sql_query_generate_w_bedrock.ipynb b/06_CodeGeneration/01_sql_query_generate_w_bedrock.ipynb new file mode 100644 index 00000000..e3961740 --- /dev/null +++ b/06_CodeGeneration/01_sql_query_generate_w_bedrock.ipynb @@ -0,0 +1,1025 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "dc40c48b-0c95-4757-a067-563cfccd51a5", + "metadata": { + "tags": [] + }, + "source": [ + "# Invoke Bedrock model for SQL Query Generation\n", + "\n", + "> *This notebook should work well with the **`Data Science 3.0`** kernel in SageMaker Studio*" + ] + }, + { + "cell_type": "markdown", + "id": "c9a413e2-3c34-4073-9000-d8556537bb6a", + "metadata": {}, + "source": [ + "## Introduction\n", + "\n", + "In this notebook we show you how to use a LLM to generate SQL Query to analyze Sales data.\n", + "\n", + "We will use Bedrock's Claude V2 model using the Boto3 API. \n", + "\n", + "The prompt used in this example is called a zero-shot prompt because we are not providing any examples of text other than the prompt.\n", + "\n", + "**Note:** *This notebook can be run within or outside of AWS environment.*\n", + "\n", + "#### Context\n", + "To demonstrate the SQL code generation capability of Amazon Bedrock, we will explore the use of Boto3 client to communicate with Amazon Bedrock API. We will demonstrate different configurations available as well as how simple input can lead to desired outputs.\n", + "\n", + "#### Pattern\n", + "We will simply provide the Amazon Bedrock API with an input consisting of a task, an instruction and an input for the model under the hood to generate an output without providing any additional example. The purpose here is to demonstrate how the powerful LLMs easily understand the task at hand and generate compelling outputs.\n", + "\n", + "![](./images/bedrock-code-gen.png)\n", + "\n", + "#### Use case\n", + "Let's take the use case to generate SQL queries to analyze sales data, focusing on trends, top products and average sales.\n", + "\n", + "#### Persona\n", + "Maya is a business analyst, at AnyCompany primarily focusing on sales and inventory data. She is transitioning from Speadsheet analysis to data-driven analysis and want to use SQL to fetch specific data points effectively. She wants to use LLMs to generate SQL queries for her analysis. \n", + "\n", + "#### Implementation\n", + "To fulfill this use case, in this notebook we will show how to generate SQL queries. We will use the Anthropic Claude v2 model using the Amazon Bedrock API with Boto3 client. " + ] + }, + { + "cell_type": "markdown", + "id": "64baae27-2660-4a1e-b2e5-3de49d069362", + "metadata": {}, + "source": [ + "## Setup\n", + "\n", + "Before running the rest of this notebook, you'll need to run the cells below to (ensure necessary libraries are installed and) connect to Bedrock.\n", + "\n", + "For more details on how the setup works and ⚠️ **whether you might need to make any changes**, refer to the [Bedrock boto3 setup notebook](../00_Intro/bedrock_boto3_setup.ipynb) notebook." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "38b791ad-e6c5-4da5-96af-5c356a36e19d", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Make sure you ran `download-dependencies.sh` from the root of the repository first!\n", + "%pip install --no-build-isolation --force-reinstall \\\n", + " ../dependencies/awscli-*-py3-none-any.whl \\\n", + " ../dependencies/boto3-*-py3-none-any.whl \\\n", + " ../dependencies/botocore-*-py3-none-any.whl\n", + "\n", + "%pip install --quiet langchain==0.0.249" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "776fd083", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "import json\n", + "import os\n", + "import sys\n", + "\n", + "import boto3\n", + "\n", + "module_path = \"..\"\n", + "sys.path.append(os.path.abspath(module_path))\n", + "from utils import bedrock, print_ww\n", + "\n", + "\n", + "# ---- ⚠️ Un-comment and edit the below lines as needed for your AWS setup ⚠️ ----\n", + "\n", + "os.environ[\"AWS_DEFAULT_REGION\"] = \"us-east-1\" # E.g. \"us-east-1\"\n", + "os.environ[\"AWS_PROFILE\"] = \"fine-tuning-bedrock\"\n", + "# os.environ[\"BEDROCK_ASSUME_ROLE\"] = \"\" # E.g. \"arn:aws:...\"\n", + "# os.environ[\"BEDROCK_ENDPOINT_URL\"] = \"\" # E.g. \"https://...\"\n", + "\n", + "\n", + "boto3_bedrock = bedrock.get_bedrock_client(\n", + " assumed_role=os.environ.get(\"BEDROCK_ASSUME_ROLE\", None),\n", + " endpoint_url=os.environ.get(\"BEDROCK_ENDPOINT_URL\", None),\n", + " region=os.environ.get(\"AWS_DEFAULT_REGION\", None),\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "4f634211-3de1-4390-8c3f-367af5554c39", + "metadata": {}, + "source": [ + "## Generate SQL Query\n", + "\n", + "Following on the use case explained above, let's prepare an input for the Amazon Bedrock service to generate SQL query." + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "id": "45ee2bae-6415-4dba-af98-a19028305c98", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# create the prompt to generate SQL query\n", + "prompt_data = \"\"\"\n", + "Command: Human: AnyCompany has a database with a table named sales_data containing sales records. The table has following columns:\n", + "- date (YYYY-MM-DD)\n", + "- product_id\n", + "- price\n", + "- units_sold\n", + "\n", + "Can you generate SQL queries for below: \n", + "- Identify the top 5 best selling products by total sales for the year 2023\n", + "- Calculate the monthly average sales for the year 2023\n", + "\n", + "Assistant:\n", + "\"\"\"\n" + ] + }, + { + "cell_type": "markdown", + "id": "cc9784e5-5e9d-472d-8ef1-34108ee4968b", + "metadata": {}, + "source": [ + "Let's start by using the Anthorpic Claude v2 model. " + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "id": "8af670eb-ad02-40df-a19c-3ed835fac8d9", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Claude - Body Syntex\n", + "body = json.dumps({\n", + " \"prompt\": prompt_data,\n", + " \"max_tokens_to_sample\":4096,\n", + " \"temperature\":0.5,\n", + " \"top_k\":250,\n", + " \"top_p\":0.5,\n", + " \"stop_sequences\": [\"\\n\\nHuman:\"]\n", + " }) " + ] + }, + { + "cell_type": "markdown", + "id": "c4ca6751", + "metadata": {}, + "source": [ + "The Amazon Bedrock API provides you with an API `invoke_model` which accepts the following:\n", + "- `modelId`: This is the model ARN for the various foundation models available under Amazon Bedrock\n", + "- `accept`: The type of input request\n", + "- `contentType`: The content type of the output\n", + "- `body`: A json string consisting of the prompt and the configurations\n", + "\n", + "Available text generation models under Amazon Bedrock have the following IDs:\n", + "- `amazon.titan-tg1-large`\n", + "- `amazon.titan-e1t-medium`\n", + "- `ai21.j2-grande-instruct`\n", + "- `ai21.j2-jumbo-instruct`\n", + "- `ai21.j2-mid`\n", + "- `ai21.j2-ultra`\n", + "- `anthropic.claude-instant-v1`\n", + "- `anthropic.claude-v1`\n", + "- `anthropic.claude-v2`" + ] + }, + { + "cell_type": "markdown", + "id": "088cf6bf-dd73-4710-a0cc-6c11d220c431", + "metadata": {}, + "source": [ + "#### Invoke the Bedrock's Claude Large Large language model" + ] + }, + { + "cell_type": "markdown", + "id": "379498f2", + "metadata": {}, + "source": [ + "First, we explore how the model generates an output based on the prompt created earlier.\n", + "\n", + "##### Complete Output Generation" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "id": "ecaceef1-0f7f-4ae5-8007-ff7c25335251", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Here are the SQL queries to answer the questions:\n", + "\n", + "1. Identify the top 5 best selling products by total sales for the year 2023:\n", + "\n", + "```sql\n", + "SELECT product_id, SUM(price * units_sold) AS total_sales\n", + "FROM sales_data\n", + "WHERE date BETWEEN '2023-01-01' AND '2023-12-31'\n", + "GROUP BY product_id\n", + "ORDER BY total_sales DESC\n", + "LIMIT 5;\n", + "```\n", + "\n", + "2. Calculate the monthly average sales for the year 2023:\n", + "\n", + "```sql\n", + "SELECT\n", + " DATE_FORMAT(date, '%Y-%m') AS month,\n", + " AVG(price * units_sold) AS avg_monthly_sales\n", + "FROM sales_data\n", + "WHERE date BETWEEN '2023-01-01' AND '2023-12-31'\n", + "GROUP BY month\n", + "ORDER BY month;\n", + "```\n", + "\n", + "The first query groups the sales data by product_id, sums the total sales for each product, filters\n", + "for 2023 data only, orders by the total sales in descending order and limits to the top 5 results.\n", + "\n", + "The second query extracts the month from the date, calculates the average monthly sales by\n", + "aggregating on the month and ordering the results chronologically.\n" + ] + } + ], + "source": [ + "modelId = 'anthropic.claude-v2' # change this to use a different version from the model provider\n", + "accept = 'application/json'\n", + "contentType = 'application/json'\n", + "\n", + "response = boto3_bedrock.invoke_model(body=body, modelId=modelId, accept=accept, contentType=contentType)\n", + "response_body = json.loads(response.get('body').read())\n", + "\n", + "print_ww(response_body.get('completion'))" + ] + }, + { + "cell_type": "markdown", + "id": "078b9db4", + "metadata": {}, + "source": [ + "### Advanced Example\n", + "#### Understanding Hospital's Patient Management System through SQL" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "id": "d439b90c", + "metadata": {}, + "outputs": [], + "source": [ + "# create the prompt\n", + "prompt_sql_data = \"\"\"Command: You're provided with a database schema representing any hospital's patient management system.\n", + "The system holds records about patients, their prescriptions, doctors, and the medications prescribed.\n", + "\n", + "Here's the schema:\n", + "\n", + "```sql\n", + "CREATE TABLE Patients (\n", + " PatientID int,\n", + " FirstName varchar(50),\n", + " LastName varchar(50),\n", + " DateOfBirth datetime,\n", + " Gender varchar(10),\n", + " PRIMARY KEY (PatientID)\n", + ");\n", + "\n", + "CREATE TABLE Doctors (\n", + " DoctorID int,\n", + " FirstName varchar(50),\n", + " LastName varchar(50),\n", + " Specialization varchar(50),\n", + " PRIMARY KEY (DoctorID)\n", + ");\n", + "\n", + "CREATE TABLE Prescriptions (\n", + " PrescriptionID int,\n", + " PatientID int,\n", + " DoctorID int,\n", + " DateIssued datetime,\n", + " PRIMARY KEY (PrescriptionID)\n", + ");\n", + "\n", + "CREATE TABLE Medications (\n", + " MedicationID int,\n", + " MedicationName varchar(50),\n", + " Dosage varchar(50),\n", + " PRIMARY KEY (MedicationID)\n", + ");\n", + "\n", + "CREATE TABLE PrescriptionDetails (\n", + " PrescriptionDetailID int,\n", + " PrescriptionID int,\n", + " MedicationID int,\n", + " Quantity int,\n", + " PRIMARY KEY (PrescriptionDetailID)\n", + ");\n", + "```\n", + "\n", + "Write a SQL query that fetches all the patients who were prescribed more than 5 different medications on 2023-04-01.\n", + "\n", + "Assistant:\n", + "\"\"\"\n" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "id": "9afa3431", + "metadata": {}, + "outputs": [], + "source": [ + "# Claude - Body Syntex\n", + "body = json.dumps({\n", + " \"prompt\": prompt_sql_data,\n", + " \"max_tokens_to_sample\":4096,\n", + " \"temperature\":0.5,\n", + " \"top_k\":250,\n", + " \"top_p\":0.5,\n", + " \"stop_sequences\": [\"\\n\\nHuman:\"]\n", + " }) " + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "id": "5c45f4fc", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Here is a SQL query to fetch patients who were prescribed more than 5 medications on 2023-04-01:\n", + "\n", + "```sql\n", + "SELECT p.FirstName, p.LastName\n", + "FROM Patients p\n", + "JOIN Prescriptions pre ON p.PatientID = pre.PatientID\n", + "JOIN PrescriptionDetails pd ON pre.PrescriptionID = pd.PrescriptionID\n", + "WHERE pre.DateIssued = '2023-04-01'\n", + "GROUP BY p.PatientID\n", + "HAVING COUNT(DISTINCT pd.MedicationID) > 5;\n", + "```\n", + "\n", + "The key steps are:\n", + "\n", + "1. Join the Patients, Prescriptions and PrescriptionDetails tables to connect patients with their\n", + "prescriptions and medication details.\n", + "\n", + "2. Filter to only prescriptions issued on 2023-04-01.\n", + "\n", + "3. Group by PatientID and count the distinct MedicationIDs per patient.\n", + "\n", + "4. Use HAVING to only keep patients with more than 5 distinct medications.\n", + "\n", + "This will return all patients who had prescriptions for more than 5 different medications on the\n", + "given date.\n" + ] + } + ], + "source": [ + "modelId = 'anthropic.claude-v2' # change this to use a different version from the model provider\n", + "accept = 'application/json'\n", + "contentType = 'application/json'\n", + "\n", + "response = boto3_bedrock.invoke_model(body=body, modelId=modelId, accept=accept, contentType=contentType)\n", + "response_body = json.loads(response.get('body').read())\n", + "\n", + "print_ww(response_body.get('completion'))" + ] + }, + { + "cell_type": "markdown", + "id": "64b08b3b", + "metadata": {}, + "source": [ + "## Conclusion\n", + "You have now experimented with using `boto3` SDK which provides a vanilla exposure to Amazon Bedrock API. Using this API you have seen the use cases of generate SQL queries to analyze sales data.\n", + "\n", + "### Take aways\n", + "- Adapt this notebook to experiment with different models available through Amazon Bedrock such as Anthropic Claude and AI21 Labs Jurassic models.\n", + "- Change the prompts to your specific usecase and evaluate the output of different models.\n", + "- Play with the token length to understand the latency and responsiveness of the service.\n", + "- Apply different prompt engineering principles to get better outputs.\n", + "\n", + "## Thank You" + ] + } + ], + "metadata": { + "availableInstances": [ + { + "_defaultOrder": 0, + "_isFastLaunch": true, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 4, + "name": "ml.t3.medium", + "vcpuNum": 2 + }, + { + "_defaultOrder": 1, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 8, + "name": "ml.t3.large", + "vcpuNum": 2 + }, + { + "_defaultOrder": 2, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 16, + "name": "ml.t3.xlarge", + "vcpuNum": 4 + }, + { + "_defaultOrder": 3, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 32, + "name": "ml.t3.2xlarge", + "vcpuNum": 8 + }, + { + "_defaultOrder": 4, + "_isFastLaunch": true, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 8, + "name": "ml.m5.large", + "vcpuNum": 2 + }, + { + "_defaultOrder": 5, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 16, + "name": "ml.m5.xlarge", + "vcpuNum": 4 + }, + { + "_defaultOrder": 6, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 32, + "name": "ml.m5.2xlarge", + "vcpuNum": 8 + }, + { + "_defaultOrder": 7, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 64, + "name": "ml.m5.4xlarge", + "vcpuNum": 16 + }, + { + "_defaultOrder": 8, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 128, + "name": "ml.m5.8xlarge", + "vcpuNum": 32 + }, + { + "_defaultOrder": 9, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 192, + "name": "ml.m5.12xlarge", + "vcpuNum": 48 + }, + { + "_defaultOrder": 10, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 256, + "name": "ml.m5.16xlarge", + "vcpuNum": 64 + }, + { + "_defaultOrder": 11, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 384, + "name": "ml.m5.24xlarge", + "vcpuNum": 96 + }, + { + "_defaultOrder": 12, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 8, + "name": "ml.m5d.large", + "vcpuNum": 2 + }, + { + "_defaultOrder": 13, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 16, + "name": "ml.m5d.xlarge", + "vcpuNum": 4 + }, + { + "_defaultOrder": 14, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 32, + "name": "ml.m5d.2xlarge", + "vcpuNum": 8 + }, + { + "_defaultOrder": 15, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 64, + "name": "ml.m5d.4xlarge", + "vcpuNum": 16 + }, + { + "_defaultOrder": 16, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 128, + "name": "ml.m5d.8xlarge", + "vcpuNum": 32 + }, + { + "_defaultOrder": 17, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 192, + "name": "ml.m5d.12xlarge", + "vcpuNum": 48 + }, + { + "_defaultOrder": 18, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 256, + "name": "ml.m5d.16xlarge", + "vcpuNum": 64 + }, + { + "_defaultOrder": 19, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 384, + "name": "ml.m5d.24xlarge", + "vcpuNum": 96 + }, + { + "_defaultOrder": 20, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": true, + "memoryGiB": 0, + "name": "ml.geospatial.interactive", + "supportedImageNames": [ + "sagemaker-geospatial-v1-0" + ], + "vcpuNum": 0 + }, + { + "_defaultOrder": 21, + "_isFastLaunch": true, + "category": "Compute optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 4, + "name": "ml.c5.large", + "vcpuNum": 2 + }, + { + "_defaultOrder": 22, + "_isFastLaunch": false, + "category": "Compute optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 8, + "name": "ml.c5.xlarge", + "vcpuNum": 4 + }, + { + "_defaultOrder": 23, + "_isFastLaunch": false, + "category": "Compute optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 16, + "name": "ml.c5.2xlarge", + "vcpuNum": 8 + }, + { + "_defaultOrder": 24, + "_isFastLaunch": false, + "category": "Compute optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 32, + "name": "ml.c5.4xlarge", + "vcpuNum": 16 + }, + { + "_defaultOrder": 25, + "_isFastLaunch": false, + "category": "Compute optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 72, + "name": "ml.c5.9xlarge", + "vcpuNum": 36 + }, + { + "_defaultOrder": 26, + "_isFastLaunch": false, + "category": "Compute optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 96, + "name": "ml.c5.12xlarge", + "vcpuNum": 48 + }, + { + "_defaultOrder": 27, + "_isFastLaunch": false, + "category": "Compute optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 144, + "name": "ml.c5.18xlarge", + "vcpuNum": 72 + }, + { + "_defaultOrder": 28, + "_isFastLaunch": false, + "category": "Compute optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 192, + "name": "ml.c5.24xlarge", + "vcpuNum": 96 + }, + { + "_defaultOrder": 29, + "_isFastLaunch": true, + "category": "Accelerated computing", + "gpuNum": 1, + "hideHardwareSpecs": false, + "memoryGiB": 16, + "name": "ml.g4dn.xlarge", + "vcpuNum": 4 + }, + { + "_defaultOrder": 30, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 1, + "hideHardwareSpecs": false, + "memoryGiB": 32, + "name": "ml.g4dn.2xlarge", + "vcpuNum": 8 + }, + { + "_defaultOrder": 31, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 1, + "hideHardwareSpecs": false, + "memoryGiB": 64, + "name": "ml.g4dn.4xlarge", + "vcpuNum": 16 + }, + { + "_defaultOrder": 32, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 1, + "hideHardwareSpecs": false, + "memoryGiB": 128, + "name": "ml.g4dn.8xlarge", + "vcpuNum": 32 + }, + { + "_defaultOrder": 33, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 4, + "hideHardwareSpecs": false, + "memoryGiB": 192, + "name": "ml.g4dn.12xlarge", + "vcpuNum": 48 + }, + { + "_defaultOrder": 34, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 1, + "hideHardwareSpecs": false, + "memoryGiB": 256, + "name": "ml.g4dn.16xlarge", + "vcpuNum": 64 + }, + { + "_defaultOrder": 35, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 1, + "hideHardwareSpecs": false, + "memoryGiB": 61, + "name": "ml.p3.2xlarge", + "vcpuNum": 8 + }, + { + "_defaultOrder": 36, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 4, + "hideHardwareSpecs": false, + "memoryGiB": 244, + "name": "ml.p3.8xlarge", + "vcpuNum": 32 + }, + { + "_defaultOrder": 37, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 8, + "hideHardwareSpecs": false, + "memoryGiB": 488, + "name": "ml.p3.16xlarge", + "vcpuNum": 64 + }, + { + "_defaultOrder": 38, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 8, + "hideHardwareSpecs": false, + "memoryGiB": 768, + "name": "ml.p3dn.24xlarge", + "vcpuNum": 96 + }, + { + "_defaultOrder": 39, + "_isFastLaunch": false, + "category": "Memory Optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 16, + "name": "ml.r5.large", + "vcpuNum": 2 + }, + { + "_defaultOrder": 40, + "_isFastLaunch": false, + "category": "Memory Optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 32, + "name": "ml.r5.xlarge", + "vcpuNum": 4 + }, + { + "_defaultOrder": 41, + "_isFastLaunch": false, + "category": "Memory Optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 64, + "name": "ml.r5.2xlarge", + "vcpuNum": 8 + }, + { + "_defaultOrder": 42, + "_isFastLaunch": false, + "category": "Memory Optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 128, + "name": "ml.r5.4xlarge", + "vcpuNum": 16 + }, + { + "_defaultOrder": 43, + "_isFastLaunch": false, + "category": "Memory Optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 256, + "name": "ml.r5.8xlarge", + "vcpuNum": 32 + }, + { + "_defaultOrder": 44, + "_isFastLaunch": false, + "category": "Memory Optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 384, + "name": "ml.r5.12xlarge", + "vcpuNum": 48 + }, + { + "_defaultOrder": 45, + "_isFastLaunch": false, + "category": "Memory Optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 512, + "name": "ml.r5.16xlarge", + "vcpuNum": 64 + }, + { + "_defaultOrder": 46, + "_isFastLaunch": false, + "category": "Memory Optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 768, + "name": "ml.r5.24xlarge", + "vcpuNum": 96 + }, + { + "_defaultOrder": 47, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 1, + "hideHardwareSpecs": false, + "memoryGiB": 16, + "name": "ml.g5.xlarge", + "vcpuNum": 4 + }, + { + "_defaultOrder": 48, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 1, + "hideHardwareSpecs": false, + "memoryGiB": 32, + "name": "ml.g5.2xlarge", + "vcpuNum": 8 + }, + { + "_defaultOrder": 49, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 1, + "hideHardwareSpecs": false, + "memoryGiB": 64, + "name": "ml.g5.4xlarge", + "vcpuNum": 16 + }, + { + "_defaultOrder": 50, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 1, + "hideHardwareSpecs": false, + "memoryGiB": 128, + "name": "ml.g5.8xlarge", + "vcpuNum": 32 + }, + { + "_defaultOrder": 51, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 1, + "hideHardwareSpecs": false, + "memoryGiB": 256, + "name": "ml.g5.16xlarge", + "vcpuNum": 64 + }, + { + "_defaultOrder": 52, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 4, + "hideHardwareSpecs": false, + "memoryGiB": 192, + "name": "ml.g5.12xlarge", + "vcpuNum": 48 + }, + { + "_defaultOrder": 53, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 4, + "hideHardwareSpecs": false, + "memoryGiB": 384, + "name": "ml.g5.24xlarge", + "vcpuNum": 96 + }, + { + "_defaultOrder": 54, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 8, + "hideHardwareSpecs": false, + "memoryGiB": 768, + "name": "ml.g5.48xlarge", + "vcpuNum": 192 + }, + { + "_defaultOrder": 55, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 8, + "hideHardwareSpecs": false, + "memoryGiB": 1152, + "name": "ml.p4d.24xlarge", + "vcpuNum": 96 + }, + { + "_defaultOrder": 56, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 8, + "hideHardwareSpecs": false, + "memoryGiB": 1152, + "name": "ml.p4de.24xlarge", + "vcpuNum": 96 + } + ], + "instance_type": "ml.t3.medium", + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.8" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/06_CodeGeneration/02_code_interpret_w_langchain.ipynb b/06_CodeGeneration/02_code_interpret_w_langchain.ipynb new file mode 100644 index 00000000..1e2d55e9 --- /dev/null +++ b/06_CodeGeneration/02_code_interpret_w_langchain.ipynb @@ -0,0 +1,967 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "af3f88dd-0f5e-427e-84ee-8934982300d1", + "metadata": { + "tags": [] + }, + "source": [ + "# Bedrock with LangChain using a Prompt that includes Context\n", + "\n", + "> *This notebook should work well with the **`Data Science 3.0`** kernel in SageMaker Studio*" + ] + }, + { + "cell_type": "markdown", + "id": "b920ca4a-a71d-4630-a6e4-577d95192ad1", + "metadata": {}, + "source": [ + "## Introduction\n", + "\n", + "In this notebook we show you how to explain or interpret a given code snippet or program.\n", + "\n", + "[LangChain](https://python.langchain.com/docs/get_started/introduction.html) is a framework for developing applications powered by language models. The key aspects of this framework allow us to augment the Large Language Models by chaining together various components to create advanced use cases.\n", + "\n", + "In this notebook we will use the Bedrock API provided by LangChain. The prompt used in this example creates a custom LangChain prompt template for adding context to the code explain request. \n", + "\n", + "**Note:** *This notebook can be run within or outside of AWS environment.*\n", + "\n", + "#### Context\n", + "In the previous example `01_sql_query_generation_w_bedrock.ipynb`, we explored how to use Bedrock API. LangChain framework to communicate with Amazon Bedrock API. In this notebook we will try to add a bit more complexity with the help of `PromptTemplates` to leverage the LangChain framework for the similar use case. `PrompTemplates` allow you to create generic shells which can be populated with information later and get model outputs based on different scenarios.\n", + "\n", + "As part of this notebook we will explore the use of Amazon Bedrock integration within LangChain framework and how it could be used to generate text with the help of `PromptTemplate`.\n", + "\n", + "#### Pattern\n", + "We will simply provide the LangChain implementation of Amazon Bedrock API with an input consisting of a task, an instruction and an input for the model under the hood to generate an output without providing any additional example. The purpose here is to demonstrate how the powerful LLMs easily understand the task at hand and generate compelling outputs.\n", + "\n", + "![](./images/bedrock-code-gen-langchain.png)\n", + "\n", + "#### Use case\n", + "To demonstrate the generation capability of models in Amazon Bedrock, let's take the use case of code explain.\n", + "\n", + "#### Persona\n", + "You are Joe, a Java software developer, has been tasked to support a legacy C++ application for Vehicle Fleet Management. You need help to explain or interpret certain complex C++ code snippets as you are performing analyis to identify the business logic and potential problems with the code.\n", + "\n", + "#### Implementation\n", + "To fulfill this use case, we will show you how you can Amazon Bedrock API with LangChain to explain C++ code snippets.\n" + ] + }, + { + "cell_type": "markdown", + "id": "aa11828a-243d-4808-9c92-e8caf4cebd37", + "metadata": {}, + "source": [ + "## Setup\n", + "\n", + "Before running the rest of this notebook, you'll need to run the cells below to (ensure necessary libraries are installed and) connect to Bedrock.\n", + "\n", + "For more details on how the setup works and ⚠️ **whether you might need to make any changes**, refer to the [Bedrock boto3 setup notebook](../00_Intro/bedrock_boto3_setup.ipynb) notebook.\n", + "\n", + "In this notebook, we'll also install the [Hugging Face Transformers](https://huggingface.co/docs/transformers/index) library which we'll use for counting the number of tokens in an input prompt." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "49e2c0a9-4838-4f2b-bb36-61c0cbcd62af", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Make sure you ran `download-dependencies.sh` from the root of the repository first!\n", + "%pip install --no-build-isolation --force-reinstall \\\n", + " ../dependencies/awscli-*-py3-none-any.whl \\\n", + " ../dependencies/boto3-*-py3-none-any.whl \\\n", + " ../dependencies/botocore-*-py3-none-any.whl\n", + "\n", + "%pip install --quiet langchain==0.0.249 \"transformers>=4.24,<5\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "558a9372-0789-414a-a1d7-2976056f2015", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "import json\n", + "import os\n", + "import sys\n", + "\n", + "import boto3\n", + "\n", + "module_path = \"..\"\n", + "sys.path.append(os.path.abspath(module_path))\n", + "from utils import bedrock, print_ww\n", + "\n", + "\n", + "# ---- ⚠️ Un-comment and edit the below lines as needed for your AWS setup ⚠️ ----\n", + "\n", + "os.environ[\"AWS_DEFAULT_REGION\"] = \"us-east-1\" # E.g. \"us-east-1\"\n", + "os.environ[\"AWS_PROFILE\"] = \"fine-tuning-bedrock\"\n", + "# os.environ[\"BEDROCK_ASSUME_ROLE\"] = \"\" # E.g. \"arn:aws:...\"\n", + "# os.environ[\"BEDROCK_ENDPOINT_URL\"] = \"\" # E.g. \"https://...\"\n", + "\n", + "\n", + "boto3_bedrock = bedrock.get_bedrock_client(\n", + " assumed_role=os.environ.get(\"BEDROCK_ASSUME_ROLE\", None),\n", + " endpoint_url=os.environ.get(\"BEDROCK_ENDPOINT_URL\", None),\n", + " region=os.environ.get(\"AWS_DEFAULT_REGION\", None),\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "b7daa1a8-d21a-410c-adbf-b253c2dabf80", + "metadata": { + "tags": [] + }, + "source": [ + "## Invoke the Bedrock LLM Model\n", + "\n", + "We'll begin with creating an instance of Bedrock class from llms. This expects a `model_id` which is the ARN of the model available in Amazon Bedrock. \n", + "\n", + "Optionally you can pass on a previously created boto3 client as well as some `model_kwargs` which can hold parameters such as `temperature`, `topP`, `maxTokenCount` or `stopSequences` (more on parameters can be explored in Amazon Bedrock console).\n", + "\n", + "Available text generation models under Amazon Bedrock have the following IDs:\n", + "\n", + "- amazon.titan-tg1-large\n", + "- ai21.j2-grande-instruct\n", + "- ai21.j2-jumbo-instruct\n", + "- anthropic.claude-instant-v1\n", + "- anthropic.claude-v1\n", + "\n", + "Note that different models support different `model_kwargs`." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "8ffa1250-56cd-4b6d-b3d8-c62baac143ce", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "from langchain.llms.bedrock import Bedrock\n", + "\n", + "inference_modifier = {'max_tokens_to_sample':4096, \n", + " \"temperature\":0.5,\n", + " \"top_k\":250,\n", + " \"top_p\":1,\n", + " \"stop_sequences\": [\"\\n\\nHuman\"]\n", + " }\n", + "\n", + "textgen_llm = Bedrock(model_id = \"anthropic.claude-v2\",\n", + " client = boto3_bedrock, \n", + " model_kwargs = inference_modifier \n", + " )\n" + ] + }, + { + "cell_type": "markdown", + "id": "de2678ed-f0d6-444f-9a57-5170dd1952f7", + "metadata": {}, + "source": [ + "## Create a LangChain custom prompt template\n", + "\n", + "By creating a template for the prompt we can pass it different input variables to it on every run. This is useful when you have to generate content with different input variables that you may be fetching from a database." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "96bc21b9", + "metadata": {}, + "outputs": [], + "source": [ + "# Vehicle Fleet Management Code written in C++\n", + "sample_code = \"\"\"\n", + "#include \n", + "#include \n", + "#include \n", + "\n", + "class Vehicle {\n", + "protected:\n", + " std::string registrationNumber;\n", + " int milesTraveled;\n", + " int lastMaintenanceMile;\n", + "\n", + "public:\n", + " Vehicle(std::string regNum) : registrationNumber(regNum), milesTraveled(0), lastMaintenanceMile(0) {}\n", + "\n", + " virtual void addMiles(int miles) {\n", + " milesTraveled += miles;\n", + " }\n", + "\n", + " virtual void performMaintenance() {\n", + " lastMaintenanceMile = milesTraveled;\n", + " std::cout << \"Maintenance performed for vehicle: \" << registrationNumber << std::endl;\n", + " }\n", + "\n", + " virtual void checkMaintenanceDue() {\n", + " if ((milesTraveled - lastMaintenanceMile) > 10000) {\n", + " std::cout << \"Vehicle: \" << registrationNumber << \" needs maintenance!\" << std::endl;\n", + " } else {\n", + " std::cout << \"No maintenance required for vehicle: \" << registrationNumber << std::endl;\n", + " }\n", + " }\n", + "\n", + " virtual void displayDetails() = 0;\n", + "\n", + " ~Vehicle() {\n", + " std::cout << \"Destructor for Vehicle\" << std::endl;\n", + " }\n", + "};\n", + "\n", + "class Truck : public Vehicle {\n", + " int capacityInTons;\n", + "\n", + "public:\n", + " Truck(std::string regNum, int capacity) : Vehicle(regNum), capacityInTons(capacity) {}\n", + "\n", + " void displayDetails() override {\n", + " std::cout << \"Truck with Registration Number: \" << registrationNumber << \", Capacity: \" << capacityInTons << \" tons.\" << std::endl;\n", + " }\n", + "};\n", + "\n", + "class Car : public Vehicle {\n", + " std::string model;\n", + "\n", + "public:\n", + " Car(std::string regNum, std::string carModel) : Vehicle(regNum), model(carModel) {}\n", + "\n", + " void displayDetails() override {\n", + " std::cout << \"Car with Registration Number: \" << registrationNumber << \", Model: \" << model << \".\" << std::endl;\n", + " }\n", + "};\n", + "\n", + "int main() {\n", + " std::vector fleet;\n", + "\n", + " fleet.push_back(new Truck(\"XYZ1234\", 20));\n", + " fleet.push_back(new Car(\"ABC9876\", \"Sedan\"));\n", + "\n", + " for (auto vehicle : fleet) {\n", + " vehicle->displayDetails();\n", + " vehicle->addMiles(10500);\n", + " vehicle->checkMaintenanceDue();\n", + " vehicle->performMaintenance();\n", + " vehicle->checkMaintenanceDue();\n", + " }\n", + "\n", + " for (auto vehicle : fleet) {\n", + " delete vehicle; \n", + " }\n", + "\n", + " return 0;\n", + "}\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "dbec103a-97ae-4e9e-9d80-dc20f354a228", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "from langchain import PromptTemplate\n", + "\n", + "# Create a prompt template that has multiple input variables\n", + "multi_var_prompt = PromptTemplate(\n", + " input_variables=[\"code\", \"programmingLanguage\"], \n", + " template=\"\"\"Human: You will be acting as an expert software developer in {programmingLanguage}. \n", + " You will explain below code and highlight if any red flags or not following best practices.\n", + " {code}\n", + " Assistant: \n", + " \"\"\"\n", + ")\n", + "\n", + "# Pass in values to the input variables\n", + "prompt = multi_var_prompt.format(code=sample_code, programmingLanguage=\"C++\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "a5b76387", + "metadata": {}, + "source": [ + "### Explain C++ Code for Vehicle Fleet management using Amazon Bedrock and LangChain" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "c1064c57-27a4-48c5-911b-e4f1dfeff122", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Overall, the code follows good OOP design principles and uses inheritance appropriately. The Vehicle\n", + "base class contains common data members and methods, while Truck and Car derive from it and add\n", + "specific details.\n", + "\n", + "Some positives:\n", + "\n", + "- Uses protected inheritance correctly to allow derived classes access to base class members.\n", + "\n", + "- Uses virtual methods like displayDetails() to enable polymorphic behavior.\n", + "\n", + "- Uses smart pointers (unique_ptr) instead of raw pointers to manage memory and avoid leaks.\n", + "\n", + "- Uses override specifier to explicitly indicate overridden methods.\n", + "\n", + "- Uses a vector to store heterogeneous objects through a common base pointer.\n", + "\n", + "- Checks for maintenance due based on miles traveled.\n", + "\n", + "- No major red flags or bad practices noted.\n", + "\n", + "Some things that could be improved:\n", + "\n", + "- The base Vehicle class could use pure virtual methods instead of a mix of virtual and pure virtual\n", + "methods.\n", + "\n", + "- The Vehicle constructor initializes data members - should consider using member initializer list\n", + "instead.\n", + "\n", + "- Unique pointers could be used instead of raw pointers for automatic memory management.\n", + "\n", + "- The displayDetails() method could be renamed to something more specific like printDetails().\n", + "\n", + "- Comments could be added to explain parts of logic/flow.\n", + "\n", + "Overall the code is well written, follows OOP principles and does not have any major issues. Just a\n", + "few minor improvements/enhancements possible.\n" + ] + } + ], + "source": [ + "response = textgen_llm(prompt)\n", + "\n", + "code_explanation = response[response.index('\\n')+1:]\n", + "\n", + "print_ww(code_explanation)" + ] + }, + { + "cell_type": "markdown", + "id": "9e9abc40", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "To conclude we learnt that invoking the LLM without any context might not yield the desired results. By adding context and further using the the prompt template to constrain the output from the LLM we are able to successfully get our desired output" + ] + } + ], + "metadata": { + "availableInstances": [ + { + "_defaultOrder": 0, + "_isFastLaunch": true, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 4, + "name": "ml.t3.medium", + "vcpuNum": 2 + }, + { + "_defaultOrder": 1, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 8, + "name": "ml.t3.large", + "vcpuNum": 2 + }, + { + "_defaultOrder": 2, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 16, + "name": "ml.t3.xlarge", + "vcpuNum": 4 + }, + { + "_defaultOrder": 3, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 32, + "name": "ml.t3.2xlarge", + "vcpuNum": 8 + }, + { + "_defaultOrder": 4, + "_isFastLaunch": true, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 8, + "name": "ml.m5.large", + "vcpuNum": 2 + }, + { + "_defaultOrder": 5, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 16, + "name": "ml.m5.xlarge", + "vcpuNum": 4 + }, + { + "_defaultOrder": 6, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 32, + "name": "ml.m5.2xlarge", + "vcpuNum": 8 + }, + { + "_defaultOrder": 7, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 64, + "name": "ml.m5.4xlarge", + "vcpuNum": 16 + }, + { + "_defaultOrder": 8, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 128, + "name": "ml.m5.8xlarge", + "vcpuNum": 32 + }, + { + "_defaultOrder": 9, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 192, + "name": "ml.m5.12xlarge", + "vcpuNum": 48 + }, + { + "_defaultOrder": 10, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 256, + "name": "ml.m5.16xlarge", + "vcpuNum": 64 + }, + { + "_defaultOrder": 11, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 384, + "name": "ml.m5.24xlarge", + "vcpuNum": 96 + }, + { + "_defaultOrder": 12, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 8, + "name": "ml.m5d.large", + "vcpuNum": 2 + }, + { + "_defaultOrder": 13, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 16, + "name": "ml.m5d.xlarge", + "vcpuNum": 4 + }, + { + "_defaultOrder": 14, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 32, + "name": "ml.m5d.2xlarge", + "vcpuNum": 8 + }, + { + "_defaultOrder": 15, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 64, + "name": "ml.m5d.4xlarge", + "vcpuNum": 16 + }, + { + "_defaultOrder": 16, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 128, + "name": "ml.m5d.8xlarge", + "vcpuNum": 32 + }, + { + "_defaultOrder": 17, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 192, + "name": "ml.m5d.12xlarge", + "vcpuNum": 48 + }, + { + "_defaultOrder": 18, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 256, + "name": "ml.m5d.16xlarge", + "vcpuNum": 64 + }, + { + "_defaultOrder": 19, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 384, + "name": "ml.m5d.24xlarge", + "vcpuNum": 96 + }, + { + "_defaultOrder": 20, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": true, + "memoryGiB": 0, + "name": "ml.geospatial.interactive", + "supportedImageNames": [ + "sagemaker-geospatial-v1-0" + ], + "vcpuNum": 0 + }, + { + "_defaultOrder": 21, + "_isFastLaunch": true, + "category": "Compute optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 4, + "name": "ml.c5.large", + "vcpuNum": 2 + }, + { + "_defaultOrder": 22, + "_isFastLaunch": false, + "category": "Compute optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 8, + "name": "ml.c5.xlarge", + "vcpuNum": 4 + }, + { + "_defaultOrder": 23, + "_isFastLaunch": false, + "category": "Compute optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 16, + "name": "ml.c5.2xlarge", + "vcpuNum": 8 + }, + { + "_defaultOrder": 24, + "_isFastLaunch": false, + "category": "Compute optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 32, + "name": "ml.c5.4xlarge", + "vcpuNum": 16 + }, + { + "_defaultOrder": 25, + "_isFastLaunch": false, + "category": "Compute optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 72, + "name": "ml.c5.9xlarge", + "vcpuNum": 36 + }, + { + "_defaultOrder": 26, + "_isFastLaunch": false, + "category": "Compute optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 96, + "name": "ml.c5.12xlarge", + "vcpuNum": 48 + }, + { + "_defaultOrder": 27, + "_isFastLaunch": false, + "category": "Compute optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 144, + "name": "ml.c5.18xlarge", + "vcpuNum": 72 + }, + { + "_defaultOrder": 28, + "_isFastLaunch": false, + "category": "Compute optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 192, + "name": "ml.c5.24xlarge", + "vcpuNum": 96 + }, + { + "_defaultOrder": 29, + "_isFastLaunch": true, + "category": "Accelerated computing", + "gpuNum": 1, + "hideHardwareSpecs": false, + "memoryGiB": 16, + "name": "ml.g4dn.xlarge", + "vcpuNum": 4 + }, + { + "_defaultOrder": 30, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 1, + "hideHardwareSpecs": false, + "memoryGiB": 32, + "name": "ml.g4dn.2xlarge", + "vcpuNum": 8 + }, + { + "_defaultOrder": 31, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 1, + "hideHardwareSpecs": false, + "memoryGiB": 64, + "name": "ml.g4dn.4xlarge", + "vcpuNum": 16 + }, + { + "_defaultOrder": 32, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 1, + "hideHardwareSpecs": false, + "memoryGiB": 128, + "name": "ml.g4dn.8xlarge", + "vcpuNum": 32 + }, + { + "_defaultOrder": 33, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 4, + "hideHardwareSpecs": false, + "memoryGiB": 192, + "name": "ml.g4dn.12xlarge", + "vcpuNum": 48 + }, + { + "_defaultOrder": 34, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 1, + "hideHardwareSpecs": false, + "memoryGiB": 256, + "name": "ml.g4dn.16xlarge", + "vcpuNum": 64 + }, + { + "_defaultOrder": 35, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 1, + "hideHardwareSpecs": false, + "memoryGiB": 61, + "name": "ml.p3.2xlarge", + "vcpuNum": 8 + }, + { + "_defaultOrder": 36, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 4, + "hideHardwareSpecs": false, + "memoryGiB": 244, + "name": "ml.p3.8xlarge", + "vcpuNum": 32 + }, + { + "_defaultOrder": 37, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 8, + "hideHardwareSpecs": false, + "memoryGiB": 488, + "name": "ml.p3.16xlarge", + "vcpuNum": 64 + }, + { + "_defaultOrder": 38, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 8, + "hideHardwareSpecs": false, + "memoryGiB": 768, + "name": "ml.p3dn.24xlarge", + "vcpuNum": 96 + }, + { + "_defaultOrder": 39, + "_isFastLaunch": false, + "category": "Memory Optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 16, + "name": "ml.r5.large", + "vcpuNum": 2 + }, + { + "_defaultOrder": 40, + "_isFastLaunch": false, + "category": "Memory Optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 32, + "name": "ml.r5.xlarge", + "vcpuNum": 4 + }, + { + "_defaultOrder": 41, + "_isFastLaunch": false, + "category": "Memory Optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 64, + "name": "ml.r5.2xlarge", + "vcpuNum": 8 + }, + { + "_defaultOrder": 42, + "_isFastLaunch": false, + "category": "Memory Optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 128, + "name": "ml.r5.4xlarge", + "vcpuNum": 16 + }, + { + "_defaultOrder": 43, + "_isFastLaunch": false, + "category": "Memory Optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 256, + "name": "ml.r5.8xlarge", + "vcpuNum": 32 + }, + { + "_defaultOrder": 44, + "_isFastLaunch": false, + "category": "Memory Optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 384, + "name": "ml.r5.12xlarge", + "vcpuNum": 48 + }, + { + "_defaultOrder": 45, + "_isFastLaunch": false, + "category": "Memory Optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 512, + "name": "ml.r5.16xlarge", + "vcpuNum": 64 + }, + { + "_defaultOrder": 46, + "_isFastLaunch": false, + "category": "Memory Optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 768, + "name": "ml.r5.24xlarge", + "vcpuNum": 96 + }, + { + "_defaultOrder": 47, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 1, + "hideHardwareSpecs": false, + "memoryGiB": 16, + "name": "ml.g5.xlarge", + "vcpuNum": 4 + }, + { + "_defaultOrder": 48, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 1, + "hideHardwareSpecs": false, + "memoryGiB": 32, + "name": "ml.g5.2xlarge", + "vcpuNum": 8 + }, + { + "_defaultOrder": 49, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 1, + "hideHardwareSpecs": false, + "memoryGiB": 64, + "name": "ml.g5.4xlarge", + "vcpuNum": 16 + }, + { + "_defaultOrder": 50, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 1, + "hideHardwareSpecs": false, + "memoryGiB": 128, + "name": "ml.g5.8xlarge", + "vcpuNum": 32 + }, + { + "_defaultOrder": 51, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 1, + "hideHardwareSpecs": false, + "memoryGiB": 256, + "name": "ml.g5.16xlarge", + "vcpuNum": 64 + }, + { + "_defaultOrder": 52, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 4, + "hideHardwareSpecs": false, + "memoryGiB": 192, + "name": "ml.g5.12xlarge", + "vcpuNum": 48 + }, + { + "_defaultOrder": 53, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 4, + "hideHardwareSpecs": false, + "memoryGiB": 384, + "name": "ml.g5.24xlarge", + "vcpuNum": 96 + }, + { + "_defaultOrder": 54, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 8, + "hideHardwareSpecs": false, + "memoryGiB": 768, + "name": "ml.g5.48xlarge", + "vcpuNum": 192 + }, + { + "_defaultOrder": 55, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 8, + "hideHardwareSpecs": false, + "memoryGiB": 1152, + "name": "ml.p4d.24xlarge", + "vcpuNum": 96 + }, + { + "_defaultOrder": 56, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 8, + "hideHardwareSpecs": false, + "memoryGiB": 1152, + "name": "ml.p4de.24xlarge", + "vcpuNum": 96 + } + ], + "instance_type": "ml.t3.medium", + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.8" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/06_CodeGeneration/03_code_translate_w_langchain.ipynb b/06_CodeGeneration/03_code_translate_w_langchain.ipynb new file mode 100644 index 00000000..8162b9e6 --- /dev/null +++ b/06_CodeGeneration/03_code_translate_w_langchain.ipynb @@ -0,0 +1,1132 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "af3f88dd-0f5e-427e-84ee-8934982300d1", + "metadata": { + "tags": [] + }, + "source": [ + "# Bedrock with LangChain - Code Translation from one programming language to another\n", + "\n", + "> *This notebook should work well with the **`Data Science 3.0`** kernel in SageMaker Studio*" + ] + }, + { + "cell_type": "markdown", + "id": "b920ca4a-a71d-4630-a6e4-577d95192ad1", + "metadata": {}, + "source": [ + "## Introduction\n", + "\n", + "In this notebook we show you how to generate an email response to a customer who was not happy with the quality of customer service that they received from the customer support engineer. We will provide additional context to the model by providing the contents of the actual email that was received from the unhappy customer.\n", + "\n", + "Because of additional context in the prompt, the text produced by the Amazon Titan Large language model in this notebook is of much better quality and relevance than the content produced earlier through zero-shot prompts.\n", + "\n", + "[LangChain](https://python.langchain.com/docs/get_started/introduction.html) is a framework for developing applications powered by language models. The key aspects of this framework allow us to augment the Large Language Models by chaining together various components to create advanced use cases.\n", + "\n", + "In this notebook we will use the Bedrock API provided by LangChain. The prompt used in this example creates a custom LangChain prompt template for adding context to the text generation request. \n", + "\n", + "**Note:** *This notebook can be run within or outside of AWS environment.*\n", + "\n", + "#### Context\n", + "In the previous example `02_code_interpret_w_langchain.ipynb`, we explored how to use LangChain framework to communicate with Amazon Bedrock API. In this notebook we will try to add a bit more complexity with the help of `PromptTemplates` to leverage the LangChain framework for the similar use case. `PrompTemplates` allow you to create generic shells which can be populated with information later and get model outputs based on different scenarios.\n", + "\n", + "As part of this notebook we will explore the use of Amazon Bedrock integration within LangChain framework and how it could be used to generate text with the help of `PromptTemplate`.\n", + "\n", + "#### Pattern\n", + "We will simply provide the LangChain implementation of Amazon Bedrock API with an input consisting of a task, an instruction and an input for the model under the hood to generate an output without providing any additional example. The purpose here is to demonstrate how the powerful LLMs easily understand the task at hand and generate compelling outputs.\n", + "\n", + "![](./images/bedrock-code-gen-langchain.png)\n", + "\n", + "#### Use case\n", + "To demonstrate the generation capability of models in Amazon Bedrock, let's take the use case of email generation.\n", + "\n", + "#### Persona\n", + "You are Bob a Customer Service Manager at AnyCompany and some of your customers are not happy with the customer service and are providing negative feedbacks on the service provided by customer support engineers. Now, you would like to respond to those customers humbly aplogizing for the poor service and regain trust. You need the help of an LLM to generate a bulk of emails for you which are human friendly and personalized to the customer's sentiment from previous email correspondence.\n", + "\n", + "#### Implementation\n", + "To fulfill this use case, we will show you how to generate an email with a thank you note based on the customer's previous email. We will use the Amazon Titan Text Large model using the Amazon Bedrock LangChain integration. \n" + ] + }, + { + "cell_type": "markdown", + "id": "aa11828a-243d-4808-9c92-e8caf4cebd37", + "metadata": {}, + "source": [ + "## Setup\n", + "\n", + "Before running the rest of this notebook, you'll need to run the cells below to (ensure necessary libraries are installed and) connect to Bedrock.\n", + "\n", + "For more details on how the setup works and ⚠️ **whether you might need to make any changes**, refer to the [Bedrock boto3 setup notebook](../00_Intro/bedrock_boto3_setup.ipynb) notebook.\n", + "\n", + "In this notebook, we'll also install the [Hugging Face Transformers](https://huggingface.co/docs/transformers/index) library which we'll use for counting the number of tokens in an input prompt." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "49e2c0a9-4838-4f2b-bb36-61c0cbcd62af", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Processing /Users/mundabra/dev/bedrock/amazon-bedrock-workshop/dependencies/awscli-1.29.21-py3-none-any.whl\n", + "Processing /Users/mundabra/dev/bedrock/amazon-bedrock-workshop/dependencies/boto3-1.28.21-py3-none-any.whl\n", + "Processing /Users/mundabra/dev/bedrock/amazon-bedrock-workshop/dependencies/botocore-1.31.21-py3-none-any.whl\n", + "Collecting docutils<0.17,>=0.10\n", + " Using cached docutils-0.16-py2.py3-none-any.whl (548 kB)\n", + "Collecting s3transfer<0.7.0,>=0.6.0\n", + " Using cached s3transfer-0.6.1-py3-none-any.whl (79 kB)\n", + "Collecting PyYAML<6.1,>=3.10\n", + " Using cached PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl (189 kB)\n", + "Collecting rsa<4.8,>=3.1.2\n", + " Using cached rsa-4.7.2-py3-none-any.whl (34 kB)\n", + "Collecting colorama<0.4.5,>=0.2.5\n", + " Using cached colorama-0.4.4-py2.py3-none-any.whl (16 kB)\n", + "Collecting urllib3<1.27,>=1.25.4\n", + " Using cached urllib3-1.26.16-py2.py3-none-any.whl (143 kB)\n", + "Collecting jmespath<2.0.0,>=0.7.1\n", + " Using cached jmespath-1.0.1-py3-none-any.whl (20 kB)\n", + "Collecting python-dateutil<3.0.0,>=2.1\n", + " Using cached python_dateutil-2.8.2-py2.py3-none-any.whl (247 kB)\n", + "Collecting six>=1.5\n", + " Using cached six-1.16.0-py2.py3-none-any.whl (11 kB)\n", + "Collecting pyasn1>=0.1.3\n", + " Using cached pyasn1-0.5.0-py2.py3-none-any.whl (83 kB)\n", + "Installing collected packages: urllib3, six, PyYAML, pyasn1, jmespath, docutils, colorama, rsa, python-dateutil, botocore, s3transfer, boto3, awscli\n", + " Attempting uninstall: urllib3\n", + " Found existing installation: urllib3 1.26.16\n", + " Uninstalling urllib3-1.26.16:\n", + " Successfully uninstalled urllib3-1.26.16\n", + " Attempting uninstall: six\n", + " Found existing installation: six 1.16.0\n", + " Uninstalling six-1.16.0:\n", + " Successfully uninstalled six-1.16.0\n", + " Attempting uninstall: PyYAML\n", + " Found existing installation: PyYAML 6.0.1\n", + " Uninstalling PyYAML-6.0.1:\n", + " Successfully uninstalled PyYAML-6.0.1\n", + " Attempting uninstall: pyasn1\n", + " Found existing installation: pyasn1 0.5.0\n", + " Uninstalling pyasn1-0.5.0:\n", + " Successfully uninstalled pyasn1-0.5.0\n", + " Attempting uninstall: jmespath\n", + " Found existing installation: jmespath 1.0.1\n", + " Uninstalling jmespath-1.0.1:\n", + " Successfully uninstalled jmespath-1.0.1\n", + " Attempting uninstall: docutils\n", + " Found existing installation: docutils 0.16\n", + " Uninstalling docutils-0.16:\n", + " Successfully uninstalled docutils-0.16\n", + " Attempting uninstall: colorama\n", + " Found existing installation: colorama 0.4.4\n", + " Uninstalling colorama-0.4.4:\n", + " Successfully uninstalled colorama-0.4.4\n", + " Attempting uninstall: rsa\n", + " Found existing installation: rsa 4.7.2\n", + " Uninstalling rsa-4.7.2:\n", + " Successfully uninstalled rsa-4.7.2\n", + " Attempting uninstall: python-dateutil\n", + " Found existing installation: python-dateutil 2.8.2\n", + " Uninstalling python-dateutil-2.8.2:\n", + " Successfully uninstalled python-dateutil-2.8.2\n", + " Attempting uninstall: botocore\n", + " Found existing installation: botocore 1.31.21\n", + " Uninstalling botocore-1.31.21:\n", + " Successfully uninstalled botocore-1.31.21\n", + " Attempting uninstall: s3transfer\n", + " Found existing installation: s3transfer 0.6.1\n", + " Uninstalling s3transfer-0.6.1:\n", + " Successfully uninstalled s3transfer-0.6.1\n", + " Attempting uninstall: boto3\n", + " Found existing installation: boto3 1.28.21\n", + " Uninstalling boto3-1.28.21:\n", + " Successfully uninstalled boto3-1.28.21\n", + " Attempting uninstall: awscli\n", + " Found existing installation: awscli 1.29.21\n", + " Uninstalling awscli-1.29.21:\n", + " Successfully uninstalled awscli-1.29.21\n", + "Successfully installed PyYAML-6.0.1 awscli-1.29.21 boto3-1.28.21 botocore-1.31.21 colorama-0.4.4 docutils-0.16 jmespath-1.0.1 pyasn1-0.5.0 python-dateutil-2.8.2 rsa-4.7.2 s3transfer-0.6.1 six-1.16.0 urllib3-1.26.16\n", + "\n", + "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip available: \u001b[0m\u001b[31;49m22.2.2\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m23.2.1\u001b[0m\n", + "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpython3.10 -m pip install --upgrade pip\u001b[0m\n", + "Note: you may need to restart the kernel to use updated packages.\n", + "\n", + "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip available: \u001b[0m\u001b[31;49m22.2.2\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m23.2.1\u001b[0m\n", + "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpython3.10 -m pip install --upgrade pip\u001b[0m\n", + "Note: you may need to restart the kernel to use updated packages.\n" + ] + } + ], + "source": [ + "# Make sure you ran `download-dependencies.sh` from the root of the repository first!\n", + "%pip install --no-build-isolation --force-reinstall \\\n", + " ../dependencies/awscli-*-py3-none-any.whl \\\n", + " ../dependencies/boto3-*-py3-none-any.whl \\\n", + " ../dependencies/botocore-*-py3-none-any.whl\n", + "\n", + "%pip install --quiet langchain==0.0.249 \"transformers>=4.24,<5\"" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "558a9372-0789-414a-a1d7-2976056f2015", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Create new client\n", + " Using region: us-east-1\n", + " Using profile: fine-tuning-bedrock\n", + "boto3 Bedrock client successfully created!\n", + "bedrock(https://bedrock.us-east-1.amazonaws.com)\n" + ] + } + ], + "source": [ + "import json\n", + "import os\n", + "import sys\n", + "\n", + "import boto3\n", + "\n", + "module_path = \"..\"\n", + "sys.path.append(os.path.abspath(module_path))\n", + "from utils import bedrock, print_ww\n", + "\n", + "\n", + "# ---- ⚠️ Un-comment and edit the below lines as needed for your AWS setup ⚠️ ----\n", + "\n", + "os.environ[\"AWS_DEFAULT_REGION\"] = \"us-east-1\" # E.g. \"us-east-1\"\n", + "os.environ[\"AWS_PROFILE\"] = \"fine-tuning-bedrock\"\n", + "# os.environ[\"BEDROCK_ASSUME_ROLE\"] = \"\" # E.g. \"arn:aws:...\"\n", + "# os.environ[\"BEDROCK_ENDPOINT_URL\"] = \"\" # E.g. \"https://...\"\n", + "\n", + "\n", + "boto3_bedrock = bedrock.get_bedrock_client(\n", + " assumed_role=os.environ.get(\"BEDROCK_ASSUME_ROLE\", None),\n", + " endpoint_url=os.environ.get(\"BEDROCK_ENDPOINT_URL\", None),\n", + " region=os.environ.get(\"AWS_DEFAULT_REGION\", None),\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "b7daa1a8-d21a-410c-adbf-b253c2dabf80", + "metadata": { + "tags": [] + }, + "source": [ + "## Invoke the Bedrock LLM Model\n", + "\n", + "We'll begin with creating an instance of Bedrock class from llms. This expects a `model_id` which is the ARN of the model available in Amazon Bedrock. \n", + "\n", + "Optionally you can pass on a previously created boto3 client as well as some `model_kwargs` which can hold parameters such as `temperature`, `topP`, `maxTokenCount` or `stopSequences` (more on parameters can be explored in Amazon Bedrock console).\n", + "\n", + "Available text generation models under Amazon Bedrock have the following IDs:\n", + "\n", + "- amazon.titan-tg1-large\n", + "- ai21.j2-grande-instruct\n", + "- ai21.j2-jumbo-instruct\n", + "- anthropic.claude-instant-v1\n", + "- anthropic.claude-v1\n", + "\n", + "Note that different models support different `model_kwargs`." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "8ffa1250-56cd-4b6d-b3d8-c62baac143ce", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "from langchain.llms.bedrock import Bedrock\n", + "\n", + "inference_modifier = {'max_tokens_to_sample':4096, \n", + " \"temperature\":0.5,\n", + " \"top_k\":250,\n", + " \"top_p\":1,\n", + " \"stop_sequences\": [\"\\n\\nHuman\"]\n", + " }\n", + "\n", + "textgen_llm = Bedrock(model_id = \"anthropic.claude-v2\",\n", + " client = boto3_bedrock, \n", + " model_kwargs = inference_modifier \n", + " )\n" + ] + }, + { + "cell_type": "markdown", + "id": "de2678ed-f0d6-444f-9a57-5170dd1952f7", + "metadata": {}, + "source": [ + "## Create a LangChain custom prompt template\n", + "\n", + "By creating a template for the prompt we can pass it different input variables to it on every run. This is useful when you have to generate content with different input variables that you may be fetching from a database.\n", + "\n", + "Previously we hardcoded the prompt, it might be the case that you have multiple customers sending similar negative feedback and you now want to use each of those customer's emails and respond to them with an apology but you also want to keep the response a bit personalized. In the following cell we are exploring how you can create a `PromptTemplate` to achieve this pattern." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "96bc21b9", + "metadata": {}, + "outputs": [], + "source": [ + "# Vehicle Fleet Management Code written in C++\n", + "sample_code = \"\"\"\n", + "#include \n", + "#include \n", + "#include \n", + "\n", + "class Vehicle {\n", + "protected:\n", + " std::string registrationNumber;\n", + " int milesTraveled;\n", + " int lastMaintenanceMile;\n", + "\n", + "public:\n", + " Vehicle(std::string regNum) : registrationNumber(regNum), milesTraveled(0), lastMaintenanceMile(0) {}\n", + "\n", + " virtual void addMiles(int miles) {\n", + " milesTraveled += miles;\n", + " }\n", + "\n", + " virtual void performMaintenance() {\n", + " lastMaintenanceMile = milesTraveled;\n", + " std::cout << \"Maintenance performed for vehicle: \" << registrationNumber << std::endl;\n", + " }\n", + "\n", + " virtual void checkMaintenanceDue() {\n", + " if ((milesTraveled - lastMaintenanceMile) > 10000) {\n", + " std::cout << \"Vehicle: \" << registrationNumber << \" needs maintenance!\" << std::endl;\n", + " } else {\n", + " std::cout << \"No maintenance required for vehicle: \" << registrationNumber << std::endl;\n", + " }\n", + " }\n", + "\n", + " virtual void displayDetails() = 0;\n", + "\n", + " ~Vehicle() {\n", + " std::cout << \"Destructor for Vehicle\" << std::endl;\n", + " }\n", + "};\n", + "\n", + "class Truck : public Vehicle {\n", + " int capacityInTons;\n", + "\n", + "public:\n", + " Truck(std::string regNum, int capacity) : Vehicle(regNum), capacityInTons(capacity) {}\n", + "\n", + " void displayDetails() override {\n", + " std::cout << \"Truck with Registration Number: \" << registrationNumber << \", Capacity: \" << capacityInTons << \" tons.\" << std::endl;\n", + " }\n", + "};\n", + "\n", + "class Car : public Vehicle {\n", + " std::string model;\n", + "\n", + "public:\n", + " Car(std::string regNum, std::string carModel) : Vehicle(regNum), model(carModel) {}\n", + "\n", + " void displayDetails() override {\n", + " std::cout << \"Car with Registration Number: \" << registrationNumber << \", Model: \" << model << \".\" << std::endl;\n", + " }\n", + "};\n", + "\n", + "int main() {\n", + " std::vector fleet;\n", + "\n", + " fleet.push_back(new Truck(\"XYZ1234\", 20));\n", + " fleet.push_back(new Car(\"ABC9876\", \"Sedan\"));\n", + "\n", + " for (auto vehicle : fleet) {\n", + " vehicle->displayDetails();\n", + " vehicle->addMiles(10500);\n", + " vehicle->checkMaintenanceDue();\n", + " vehicle->performMaintenance();\n", + " vehicle->checkMaintenanceDue();\n", + " }\n", + "\n", + " for (auto vehicle : fleet) {\n", + " delete vehicle; \n", + " }\n", + "\n", + " return 0;\n", + "}\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "dbec103a-97ae-4e9e-9d80-dc20f354a228", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "from langchain import PromptTemplate\n", + "\n", + "# Create a prompt template that has multiple input variables\n", + "multi_var_prompt = PromptTemplate(\n", + " input_variables=[\"code\", \"srcProgrammingLanguage\", \"targetProgrammingLanguage\"], \n", + " template=\"\"\"Human: You will be acting as an expert software developer in {srcProgrammingLanguage} and {targetProgrammingLanguage}. \n", + " You will tranlslate below code from {srcProgrammingLanguage} to {targetProgrammingLanguage} while following coding best practices.\n", + " {code}\n", + " Assistant: \n", + " \"\"\"\n", + ")\n", + "\n", + "# Pass in values to the input variables\n", + "prompt = multi_var_prompt.format(code=sample_code, srcProgrammingLanguage=\"C++\", targetProgrammingLanguage=\"Java\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "a5b76387", + "metadata": {}, + "source": [ + "### Code translation from C++ to Java" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "c1064c57-27a4-48c5-911b-e4f1dfeff122", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "```java\n", + "import java.util.ArrayList;\n", + "\n", + "class Vehicle {\n", + " protected String registrationNumber;\n", + " protected int milesTraveled;\n", + " protected int lastMaintenanceMile;\n", + "\n", + " public Vehicle(String regNum) {\n", + " this.registrationNumber = regNum;\n", + " this.milesTraveled = 0;\n", + " this.lastMaintenanceMile = 0;\n", + " }\n", + "\n", + " public void addMiles(int miles) {\n", + " this.milesTraveled += miles;\n", + " }\n", + "\n", + " public void performMaintenance() {\n", + " this.lastMaintenanceMile = this.milesTraveled;\n", + " System.out.println(\"Maintenance performed for vehicle: \" + this.registrationNumber);\n", + " }\n", + "\n", + " public void checkMaintenanceDue() {\n", + " if ((this.milesTraveled - this.lastMaintenanceMile) > 10000) {\n", + " System.out.println(\"Vehicle: \" + this.registrationNumber + \" needs maintenance!\");\n", + " } else {\n", + " System.out.println(\"No maintenance required for vehicle: \" + this.registrationNumber);\n", + " }\n", + " }\n", + "\n", + " public void displayDetails() {\n", + " // Implemented in subclasses\n", + " }\n", + "}\n", + "\n", + "class Truck extends Vehicle {\n", + " private int capacityInTons;\n", + "\n", + " public Truck(String regNum, int capacity) {\n", + " super(regNum);\n", + " this.capacityInTons = capacity;\n", + " }\n", + "\n", + " @Override\n", + " public void displayDetails() {\n", + " System.out.println(\"Truck with Registration Number: \" + this.registrationNumber + \",\n", + "Capacity: \" + this.capacityInTons + \" tons.\");\n", + " }\n", + "}\n", + "\n", + "class Car extends Vehicle {\n", + " private String model;\n", + "\n", + " public Car(String regNum, String carModel) {\n", + " super(regNum);\n", + " this.model = carModel;\n", + " }\n", + "\n", + " @Override\n", + " public void displayDetails() {\n", + " System.out.println(\"Car with Registration Number: \" + this.registrationNumber + \", Model: \"\n", + "+ this.model + \".\");\n", + " }\n", + "}\n", + "\n", + "public class Main {\n", + " public static void main(String[] args) {\n", + " ArrayList fleet = new ArrayList<>();\n", + "\n", + " fleet.add(new Truck(\"XYZ1234\", 20));\n", + " fleet.add(new Car(\"ABC9876\", \"Sedan\"));\n", + "\n", + " for (Vehicle vehicle : fleet) {\n", + " vehicle.displayDetails();\n", + " vehicle.addMiles(10500);\n", + " vehicle.checkMaintenanceDue();\n", + " vehicle.performMaintenance();\n", + " vehicle.checkMaintenanceDue();\n", + " }\n", + " }\n", + "}\n", + "```\n", + "\n", + "Key points:\n", + "\n", + "- Used ArrayList instead of raw vectors\n", + "- Overrode methods using @Override annotation\n", + "- Used access modifiers properly (private, public)\n", + "- Followed naming conventions and formatting standards\n", + "- Implemented polymorphic behavior using abstract class and subclasses\n", + "\n", + "Let me know if you have any other questions!\n" + ] + } + ], + "source": [ + "response = textgen_llm(prompt)\n", + "\n", + "target_code = response[response.index('\\n')+1:]\n", + "\n", + "print_ww(target_code)" + ] + }, + { + "cell_type": "markdown", + "id": "9e9abc40", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "To conclude we learnt that invoking the LLM without any context might not yield the desired results. By adding context and further using the the prompt template to constrain the output from the LLM we are able to successfully get our desired output" + ] + } + ], + "metadata": { + "availableInstances": [ + { + "_defaultOrder": 0, + "_isFastLaunch": true, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 4, + "name": "ml.t3.medium", + "vcpuNum": 2 + }, + { + "_defaultOrder": 1, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 8, + "name": "ml.t3.large", + "vcpuNum": 2 + }, + { + "_defaultOrder": 2, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 16, + "name": "ml.t3.xlarge", + "vcpuNum": 4 + }, + { + "_defaultOrder": 3, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 32, + "name": "ml.t3.2xlarge", + "vcpuNum": 8 + }, + { + "_defaultOrder": 4, + "_isFastLaunch": true, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 8, + "name": "ml.m5.large", + "vcpuNum": 2 + }, + { + "_defaultOrder": 5, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 16, + "name": "ml.m5.xlarge", + "vcpuNum": 4 + }, + { + "_defaultOrder": 6, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 32, + "name": "ml.m5.2xlarge", + "vcpuNum": 8 + }, + { + "_defaultOrder": 7, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 64, + "name": "ml.m5.4xlarge", + "vcpuNum": 16 + }, + { + "_defaultOrder": 8, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 128, + "name": "ml.m5.8xlarge", + "vcpuNum": 32 + }, + { + "_defaultOrder": 9, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 192, + "name": "ml.m5.12xlarge", + "vcpuNum": 48 + }, + { + "_defaultOrder": 10, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 256, + "name": "ml.m5.16xlarge", + "vcpuNum": 64 + }, + { + "_defaultOrder": 11, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 384, + "name": "ml.m5.24xlarge", + "vcpuNum": 96 + }, + { + "_defaultOrder": 12, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 8, + "name": "ml.m5d.large", + "vcpuNum": 2 + }, + { + "_defaultOrder": 13, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 16, + "name": "ml.m5d.xlarge", + "vcpuNum": 4 + }, + { + "_defaultOrder": 14, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 32, + "name": "ml.m5d.2xlarge", + "vcpuNum": 8 + }, + { + "_defaultOrder": 15, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 64, + "name": "ml.m5d.4xlarge", + "vcpuNum": 16 + }, + { + "_defaultOrder": 16, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 128, + "name": "ml.m5d.8xlarge", + "vcpuNum": 32 + }, + { + "_defaultOrder": 17, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 192, + "name": "ml.m5d.12xlarge", + "vcpuNum": 48 + }, + { + "_defaultOrder": 18, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 256, + "name": "ml.m5d.16xlarge", + "vcpuNum": 64 + }, + { + "_defaultOrder": 19, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 384, + "name": "ml.m5d.24xlarge", + "vcpuNum": 96 + }, + { + "_defaultOrder": 20, + "_isFastLaunch": false, + "category": "General purpose", + "gpuNum": 0, + "hideHardwareSpecs": true, + "memoryGiB": 0, + "name": "ml.geospatial.interactive", + "supportedImageNames": [ + "sagemaker-geospatial-v1-0" + ], + "vcpuNum": 0 + }, + { + "_defaultOrder": 21, + "_isFastLaunch": true, + "category": "Compute optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 4, + "name": "ml.c5.large", + "vcpuNum": 2 + }, + { + "_defaultOrder": 22, + "_isFastLaunch": false, + "category": "Compute optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 8, + "name": "ml.c5.xlarge", + "vcpuNum": 4 + }, + { + "_defaultOrder": 23, + "_isFastLaunch": false, + "category": "Compute optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 16, + "name": "ml.c5.2xlarge", + "vcpuNum": 8 + }, + { + "_defaultOrder": 24, + "_isFastLaunch": false, + "category": "Compute optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 32, + "name": "ml.c5.4xlarge", + "vcpuNum": 16 + }, + { + "_defaultOrder": 25, + "_isFastLaunch": false, + "category": "Compute optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 72, + "name": "ml.c5.9xlarge", + "vcpuNum": 36 + }, + { + "_defaultOrder": 26, + "_isFastLaunch": false, + "category": "Compute optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 96, + "name": "ml.c5.12xlarge", + "vcpuNum": 48 + }, + { + "_defaultOrder": 27, + "_isFastLaunch": false, + "category": "Compute optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 144, + "name": "ml.c5.18xlarge", + "vcpuNum": 72 + }, + { + "_defaultOrder": 28, + "_isFastLaunch": false, + "category": "Compute optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 192, + "name": "ml.c5.24xlarge", + "vcpuNum": 96 + }, + { + "_defaultOrder": 29, + "_isFastLaunch": true, + "category": "Accelerated computing", + "gpuNum": 1, + "hideHardwareSpecs": false, + "memoryGiB": 16, + "name": "ml.g4dn.xlarge", + "vcpuNum": 4 + }, + { + "_defaultOrder": 30, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 1, + "hideHardwareSpecs": false, + "memoryGiB": 32, + "name": "ml.g4dn.2xlarge", + "vcpuNum": 8 + }, + { + "_defaultOrder": 31, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 1, + "hideHardwareSpecs": false, + "memoryGiB": 64, + "name": "ml.g4dn.4xlarge", + "vcpuNum": 16 + }, + { + "_defaultOrder": 32, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 1, + "hideHardwareSpecs": false, + "memoryGiB": 128, + "name": "ml.g4dn.8xlarge", + "vcpuNum": 32 + }, + { + "_defaultOrder": 33, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 4, + "hideHardwareSpecs": false, + "memoryGiB": 192, + "name": "ml.g4dn.12xlarge", + "vcpuNum": 48 + }, + { + "_defaultOrder": 34, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 1, + "hideHardwareSpecs": false, + "memoryGiB": 256, + "name": "ml.g4dn.16xlarge", + "vcpuNum": 64 + }, + { + "_defaultOrder": 35, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 1, + "hideHardwareSpecs": false, + "memoryGiB": 61, + "name": "ml.p3.2xlarge", + "vcpuNum": 8 + }, + { + "_defaultOrder": 36, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 4, + "hideHardwareSpecs": false, + "memoryGiB": 244, + "name": "ml.p3.8xlarge", + "vcpuNum": 32 + }, + { + "_defaultOrder": 37, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 8, + "hideHardwareSpecs": false, + "memoryGiB": 488, + "name": "ml.p3.16xlarge", + "vcpuNum": 64 + }, + { + "_defaultOrder": 38, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 8, + "hideHardwareSpecs": false, + "memoryGiB": 768, + "name": "ml.p3dn.24xlarge", + "vcpuNum": 96 + }, + { + "_defaultOrder": 39, + "_isFastLaunch": false, + "category": "Memory Optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 16, + "name": "ml.r5.large", + "vcpuNum": 2 + }, + { + "_defaultOrder": 40, + "_isFastLaunch": false, + "category": "Memory Optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 32, + "name": "ml.r5.xlarge", + "vcpuNum": 4 + }, + { + "_defaultOrder": 41, + "_isFastLaunch": false, + "category": "Memory Optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 64, + "name": "ml.r5.2xlarge", + "vcpuNum": 8 + }, + { + "_defaultOrder": 42, + "_isFastLaunch": false, + "category": "Memory Optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 128, + "name": "ml.r5.4xlarge", + "vcpuNum": 16 + }, + { + "_defaultOrder": 43, + "_isFastLaunch": false, + "category": "Memory Optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 256, + "name": "ml.r5.8xlarge", + "vcpuNum": 32 + }, + { + "_defaultOrder": 44, + "_isFastLaunch": false, + "category": "Memory Optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 384, + "name": "ml.r5.12xlarge", + "vcpuNum": 48 + }, + { + "_defaultOrder": 45, + "_isFastLaunch": false, + "category": "Memory Optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 512, + "name": "ml.r5.16xlarge", + "vcpuNum": 64 + }, + { + "_defaultOrder": 46, + "_isFastLaunch": false, + "category": "Memory Optimized", + "gpuNum": 0, + "hideHardwareSpecs": false, + "memoryGiB": 768, + "name": "ml.r5.24xlarge", + "vcpuNum": 96 + }, + { + "_defaultOrder": 47, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 1, + "hideHardwareSpecs": false, + "memoryGiB": 16, + "name": "ml.g5.xlarge", + "vcpuNum": 4 + }, + { + "_defaultOrder": 48, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 1, + "hideHardwareSpecs": false, + "memoryGiB": 32, + "name": "ml.g5.2xlarge", + "vcpuNum": 8 + }, + { + "_defaultOrder": 49, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 1, + "hideHardwareSpecs": false, + "memoryGiB": 64, + "name": "ml.g5.4xlarge", + "vcpuNum": 16 + }, + { + "_defaultOrder": 50, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 1, + "hideHardwareSpecs": false, + "memoryGiB": 128, + "name": "ml.g5.8xlarge", + "vcpuNum": 32 + }, + { + "_defaultOrder": 51, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 1, + "hideHardwareSpecs": false, + "memoryGiB": 256, + "name": "ml.g5.16xlarge", + "vcpuNum": 64 + }, + { + "_defaultOrder": 52, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 4, + "hideHardwareSpecs": false, + "memoryGiB": 192, + "name": "ml.g5.12xlarge", + "vcpuNum": 48 + }, + { + "_defaultOrder": 53, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 4, + "hideHardwareSpecs": false, + "memoryGiB": 384, + "name": "ml.g5.24xlarge", + "vcpuNum": 96 + }, + { + "_defaultOrder": 54, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 8, + "hideHardwareSpecs": false, + "memoryGiB": 768, + "name": "ml.g5.48xlarge", + "vcpuNum": 192 + }, + { + "_defaultOrder": 55, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 8, + "hideHardwareSpecs": false, + "memoryGiB": 1152, + "name": "ml.p4d.24xlarge", + "vcpuNum": 96 + }, + { + "_defaultOrder": 56, + "_isFastLaunch": false, + "category": "Accelerated computing", + "gpuNum": 8, + "hideHardwareSpecs": false, + "memoryGiB": 1152, + "name": "ml.p4de.24xlarge", + "vcpuNum": 96 + } + ], + "instance_type": "ml.t3.medium", + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.8" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/06_CodeGeneration/README.md b/06_CodeGeneration/README.md new file mode 100644 index 00000000..0dde55a1 --- /dev/null +++ b/06_CodeGeneration/README.md @@ -0,0 +1,37 @@ +# Lab 6 - Code Generation + +## Overview + +In this lab, you will learn to use LLMs on Amazon Bedrock for code generation, SQL query creation, code explanation, and code translation across languages. We will demo Bedrock's API (boto3) as well as its integration with LangChain. + +First, we will generate Python code and SQL queries by providing context about a dataset. Next, we will explain code and translate between languages. We will explore these use cases with both the Bedrock API directly and via LangChain integration. + +## Audience + +Architects and developers who want to learn how to use Amazon Bedrock LLMs to generate, explain and translate code. + +Some of the business use cases for code generation include: + +- Code Translation +- Code Explain and Reviews +- Database or SQL query generation +- Rapid Prototyping +- Issue Identification +- Bug Fixing +- Code Optimization + +## Workshop Notebooks + +1. [Code Generation](./00_code_generatation_w_bedrock.ipynb)- Demonstrates how to generate Python code using Natural language. It shows examples of prompting to generate simple functions, classes, and full programs in Python for Data Analyst to perform sales analysis on a given Sales CSV dataset. + +2. [Database or SQL Query Generation](./01_sql_query_generate_w_bedrock.ipynb) - Focuses on generating SQL queries with Amazon Bedrock APIs. It includes examples of generating both simple and complex SQL statements for a given data set and database schema. + +3. [Code Explanation](./02_code_interpret_w_langchain.ipynb) - Uses Bedrock's foundation models to generate explanations for complex C++ code snippets. It shows how to carefully craft prompts to get the model to generate comments and documentation that explain the functionality and logic of complicated C++ code examples. Prompts can be easily updated for another programming languages. + +4. [Code Translation ](./03_code_translate_w_langchain.ipynb) - Guides you through translating C++ code to Java using Amazon Bedrock and LangChain APIs. It shows techniques for prompting the model to port C++ code over to Java, handling differences in syntax, language constructs, and conventions between the languages. + + +## Architecture + +![Bedrock](./images/bedrock-code-gen.png) +![Bedrock](./images/bedrock-code-gen-langchain.png) \ No newline at end of file diff --git a/06_CodeGeneration/images/bedrock-code-gen-langchain.png b/06_CodeGeneration/images/bedrock-code-gen-langchain.png new file mode 100644 index 0000000000000000000000000000000000000000..d829e6fd6efd3afc4c20d4e66db539f947e6be80 GIT binary patch literal 71989 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

C!4^U-(G|N_ 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 literal 0 HcmV?d00001 diff --git a/06_CodeGeneration/images/bedrock-code-gen.png b/06_CodeGeneration/images/bedrock-code-gen.png new file mode 100644 index 0000000000000000000000000000000000000000..3457eac256c423d18eead46d67fff7a39ce8e04d GIT binary patch literal 56538 zcmeEOg;!Kt+eb)B8Ip4jKTl7b}eZPME)C@8qnQerA7C>UfYD5$Ymn7|cY z)00&clp8u$FJ34~zj#5d>0YRGx zu(|Jm`=*)?Uyb2myaPYty{R(H$5Eg<4AbaUL|Xh?rCRy57`+c(DZB$yv_Cd6sfIYV zi}4RXt@QWl1e}PtwCJ9wVK0=B9Sz;F_LO9AV3k~*!J!FY)%*PYk+GVv`7r$4-B*z6 zI|#j;I$ZR&)q~o5DYRkYp9G)+V7I9!o@~vV*-5_pLpv_^e(xo}yZ6P{p(^ZEdQ>ez zgPo)9DrIBITMOswnp;u4HZ9}k%5>dIV;?^JcGYmzPIj06t@(+z7HlK?gqF0A8fP3k3!3(_56A!2i3zODqNT@>dM96tv6F zsIeC}imJYlmInT-nmC!6**jZ0fIp2A83SDnS*dA(wdCbMCJuJYFHIec&6wTn950%n z2)KcOk9KC@m(*@{w)V~-H$j>|?*IXxFD|ptQ2%)g*hY{>OJ0fkg@cnBH8(RWGb@eI zZE9+20Vh*)kcybZ@8-ZaK^jXi*b&6S;_B+k?8?FH;AFwV#>dCU!phFV&dvnf!Q||2 z4}R&!WbaJ-pH43O5i@f(ak6p*TRGTMU-bLZ*Z~3-q@lSO=<4r3^E7j_`fDV6=ikc$ z7RYk(3kw@FE6Y{iKvRK>s~{ySH#1vpF)KTOJir)2+}vCOf8PI(U;Y~LzglYj)sl^a zmFK^k{?|{}o2omTIlXYO1I7dk{k1i}8~^u@zZ(j$TrB;+Nbw&!|9KT4w9st1W;f;bKf+8&@s^)fM1#$B;k$QRKCfbYpm~U|cpHf?)+z<`;z~}et!}L;4&6k2# zkRJm-)V05U2`KnNgQ+2c5p47Q#|lFhwG<{TA1>d*&N7j%;DDe$MQ%-Q_4)qsJOnb3 zue*O{&sNXp`6Bj7ehoSPT~sV;Ka_vIg{6y(JX&9HLB|(Cx$)1pMk4Cr){pMUf6aM_)oBA2iuK&w`7kyRIh!pkQlejJVZ$qNo2!Ph2{rhaEKA?P)(0o9T{x4Gn z`oiMgy7RBA^z&mxMJLmLnU4R@C0@({SS0`d7Wu!O{HJ{Uk6r&GYX6hT|3`{PsVhq5 z=^xIhQ4H#laTpjOyE+Xo16RhY+BZSO_#)K1sAg@E;k0A-rTd;hAG5G1-e)$T;jrk8 zsPo*bjiMRZi(1f)L}aQ%|Z9u z!dfW9_EtyH1uf44#GRbaYs}U{g>NPbaCOW94=c5Hjwzqg}G#WN3*|kFmt@;dDN=%WOLYRKw@cr}DiW-C|yV_UTaoHCZ%q}+?gJ;zJ%)u)cnTs+HyrW+w5hg)-V zkr5#gYx;daB3MlK5~yg+7DxdLNyMGULJ@*P*<;C4 zxtR)0*N-1$p9><{>m#SVW^kt5{qwN7t;06k+I?6w9|#wtKUft^|CUoANo97n!&eiN zFpfk$HVYACNI|L^fY11xI+M?+ahHca=0?x8MR5$a^UD>~EVFN0jBG`14f0d9s>070 z3!GYmMFdcTxP&;Z_nJs~XoerCzsI>o1Ovj>9HW_@L{Mwd!%(TtxGu6Zw|ulLMJH4c zXk~XY+wc+ilYK~gAUXH&*?P-1O_Su2T!VASk zwKGer2(}-|udkVZze)6xC&LH63yDm46WgvmJ@3=U@p&8I+FRXqu2OGq@3`CVRPyL# z+xR7czZbt`Oy>n~8}*!gs*@A@GbskJ-0chYOBiYIiAud%;L8fQWB6>t+1$HuyuTiEYf>S4(nr@4^VNM)fAZC#5BC_Q5=YBAsPvJ=&MkXiklq^k z9#V0-O*1D?yE`(B(#q@H2CUuZt71-CMG^_F;Xf?KDWuU1Umcv)_3oW9_OYM4NS@Zy zP=SZ*p~u56gNLjvg?Wq2KOU{$vP`D((b`G$@;y4_A#q%9V)vd;D1vr3eigJzCN8t5 zM+75Uhd8_k6q;VW6X7t~clsw5eNDV!CzdZ4%kH!r<%>1RFgCI;d?L2yxyUwB^eST2 zcDBj;c(1+QyN1@HM1R04s)eFY9c(eHQ+7K&CFQLer^J*Ur%S}Z0w98V^@*I~PrT0q zZ6^&ppxnC3%6MGeE*fyv47+#@l|p-pZ1dpez8+N^;!N5&inY&W9zI+>6rD{OQO#1;-tfHiG#f~yU1h23y3qC{q|!!VLFLf1Rd)T0 z>M>vF-vxIH*PmTp$CYmin)8R|N6HqLC}kF*dz(*iJrYM2KBWk6&`#hl{2&ZLGFOa) z^Gj{UK$-lIpb^cM`o~Rg8_4(P@pglae%7I#J+Vk|QTfBds>p8q0B(8|JmR#USiw7A zcV$Zu)hVKx9B^Lov!fUrV^Z-PxbMvB{#_Sgy2J7s z+WA-8#thyYnC(xIQ%5hSt1jftwKYYQ(lny zENfbU>h626Q#1tCZZN+sk6*v0fhUMIR$H4Xo29;6E=_+^%y`tD zl@?Cvc|hs>`ei9-xX?yKOmbe|?c4fnbHfBZVZ-T!&&Xz_cMUhY^#Q(#ng&;kz#stNBm}bC05alhV*1{f+i{(#gI(7z?fRbh}|SYgCV@e2>iXDkM6GnjWZ=(65es zm82wNf2ML@voK>6=2KVw5;>M{LuX}|#{@cxlfm&6Ti=FV2@|Fv z+JG+iX<3jjp4pN9P5=+&n!0%>p8{yv+8E?w*G;s$3S50w?I4|>tLe1)%^@DlYa(fN zl%$;GnPN>&Hqf_67Y2WB$^RI;(}Nq_e0m&Ji3-)IfmHAAY8sk*LrywOk9Eo{nH%_# zU=F_rvMI`BsqOb4#e3+?Pbhv%MKCG4DWM+bB z)F2RVso%8!1Nq*7+-)*(-ox?zgaaZ5unBmR!i{?8-5}z%?KOUTsFj#WrDp`+(0X%j zlfpCr`mUAx+S*mz+ClKoLyKnGwF2U1xl&40=owiIP4e$T`YHKF*DYnDfacB-9=%tj3!np50Ad}FjT zJjT{YQ08DdxlUo!ifnqL{k=`l5Tmjw(Z668Jomhkm}CdL!>&Y_DQU#h3W*DxuVv!R0CT$?qGHQgyb`tM7*)7Q|?KlTt$f3nAq zvTgfgJyi>t@WGXB&>{lw@A$ltAGIEbTLT_SZaZELiMJgu9Z{bn^Z@gqxhuR`8>>*X zZ$LhDKSto8Y1b$5{v3}z{9!>ABZb5@7}@`&@=XE}nW6d`m&IjeoZh#7$)!i+T~su) z?n_Lq(mM+~YxIgZ)$nSfZ5SpFWN5)rI?%nLVFlEa=(H-0#&>F8CtH8El31i#AOGOS zfT`;4No)L!m`?8IA2Xp*{`&|5&3mQ!i6Mge{Vu(}kf#tv&7vZA=4*&M* zx3jO|2{5FMlK0{^E$SgcNNOLT=yGz7D_gWh#La6I^+c^tkkA)wYi-V;|E%C?9BM+d zmxRf>;xEDY6;tqheBFgZ$~Y1G)K<4&1q@CROvImaFAR2PeA;cWV&~)IEP# zadK*~sdiOK|=#0y+EM1&h=MQpvJ-ZHfo`;>OX^R6eLDF=CW=A4D5-}NR&xN)0Z z3+SaArtEa*?+iU$qg&=&lFCkH8|djwubC;go#-fT#lR+RuTA!ORa-zTbO47rHCxf~ z&C^>CJQq4jfArba@-?o2&mpA#nO^B1#d)U8i=>|vmM-kOs=~@NSo?!cg=4KogMoev z7b=BAiT-dolTLC_@_k#z?@=!bk6v>#k2ZvNpA5P-l)`fyo+X>LM@N0Q$MB9>&`6Qr z^>M@7FLkwQR2IMX!^rGpcJ#qBBa!}phf-laHw-b!(&PyS>3>E+!yQwY=AlFoM7b;gb)I8bAkY?l^Jrtv(PYR12Hu8-^W?p%R)o{l@HJ<4 z>eAd}fs%5+b3^2RG}uk`evG<0S{|JQOL|njW+iCioB}xi^PD~ur`29RWK1{!=x0&f zU#JV4eL_WU2FwyqD~slug=j(dBC#DOf4q_2482W;?h+5LoVs_}B)F^?A{&uG-|;*aZWIT{qkf zgQMeZ*+f^+v>6o))Z#p`zS>tNN>#hC?qs{aK}NpR%)%5*&~`M_VtP(NP;l1c)3PYo zvnA#!QMurB=kLOaa-+tNB@kMD{Hj!K(YQ!iQd&Z$SdK6lm|ZXcro}xHR)z@#C^(Jh zd6@_Ev}r@eH_h}b>|c2Cyq+CI1kz>zd>I$N^4gPUzT;GShmMoW&Phw}n*_`8>vE>> zbsu}f{er~VjD;j3#4w$1#Hp)TCAY*eMcTetJK-wHbW%ItHRF%Ig4*RAxLPIPk{2&w zJ6uU`oDcREt>`UZ9izRkPTRIN?BNV0JtSx^a&R;jzT>JYD!LJgbacPVS7AL6CuaZA z0}jMG9*u-UWuuuxM;9brm+{M(f0}NTD6+4XtnJzy$MM+Ez=Z{QbJ3uql7MiS))9Nr zTys?ML;YAJxhZ)aMKa-^ndp+;5=?Yic=v7Ht@^bzGgTbMD@Qi@JRXipiFK;hxXku^ z*t7cHBXMIHJf!vrcgk8xY3MC>3?^C+@wL|G+5Jiq*{G% zWrTLFJqAx#W99|f8$kzQ_sZfVz4TI_7t$j3H3Z##opuSCoXa8Ks$X`VogUNPi|sBG z{}|mCzWU+&1V0dK**fiCdRdq;GfOlx5*`2rF&*un4@pWi?76h%%$QN}kgitj-6Q3P zC%bHIhR7!aMr9m~L~(xcxmzwd*i%2m(!*`h3htx0zy1*ZT#4=4?ki#+IFXip@ZmjE zH9^;%H5#LDep<=Ma$FZlulgigp<5Z^Jb${P^%hg0n$kOR22$rZ!Dk^+D`e9ZDmtVo zHax8($$b&-5Isa3m012{Z~0Q~y1L@VL~)_O=*+u$dcWG&Z(3Uor}(5llh)rZQB=V_ zp6v9oF+@gp(ykGm$LM~)Ns1`CttnCE%l^uVlCv*rCCve9q(?UHKNa@yIeBOHp0GiQ&fe12&O*QKbM3X|-| z={e6)1=E+T-SnwlAtZRx{L#l|G8IY+DwpHI)+sI--t?d${vzKKTFU4_1IMnTLo@_E zPlmf*BQg4yTL;|3S-50E)^eqLu|xb60|r7T`?*_xq#Vz_wJ8iUfF(a;(&gkBw97Ta zdW>~A|g6v7t&Y20_=`;$piC=+NV+9q%zi2QeUCw{hjEV zl!rN^*6V*&_Oy;Gwx7v0nfxTI=Ox-7k_%w=guWV!ey=^8dyHyWUIPT;92W3o+}W+c3C0%e-x*^{fYOs#llb_EGk!{ zJ(@|^Y<;3mB}3+3wq2=uU?K&7X@rZN&c|wB`&nm`Rk{3mmWHkvx@31zFvn-hZ1HXL zt>LD9^b)(#+8@JH=CESrR_h)SH5#&2Hw0bxFgo7JVEyDg3)nUrvxRH%q_Ig6eUN8h zsy4)|={09vxGw?0U;%z)F6>ZdH zoeiBy8yF^~9Z+C}vfW--e+vXN1Xs_e&?QAlQa(^xjyrv=KnvxJ>QCYsoViKq{@jCn zY~a-kawbX1kIRbBvICeb$z;zg-IKguri0h|4HS3_JWKOLhjPijX`M>Dq-P9y2aqN< z5(}5D!;*bJ4u7wYF?ibYzy`V;xy+!>5TAA@k=x^gR@mwEJk6o{sh1ndxZE~^XcPkThx&;kO+=pD>vFIw|wkB^)w#l+I%xk=w zf7cjqe;Tydj5pmIe|{zc=0Skza5GPAWwqI>t3eYUn1c~tj;r7%1ZbSemws(IMnZo`a^^CQP+t4_3`_9~q2ZJazGWGaKf zXOuFPpT^%^17Q)U+61=!Nqtj@GM>GdF-!p<#K$D9ZLtP~L+mRL@3`9^wEIJ*et8dn z%4nYpkS+Z5e$t0(ckr=n%ukFMy~o05CpP`H8kmUzg5$$d;zK-goOYA-FMNDQAVA=K z<}MSBqhTq97aGZoK)2Jn_afV2Qxs2IB$fFe?yd(bx)t^juV#d!WD6@r!B9!q>`f|I zb&J!4;%eV6L9*BU53RU+t!Xw1%it5SKH)V_wbEd@3_u;UsqworU$dR(=w1!xX^DE0 zsMd}vlk2A~f>n$>tml0)+gGs2s!4(i{TtCD7m+| zg{+BqA7#@?85kH$Rrh^s`=xFkwgq@nA1l-B7Ef&k3pgb0lU%!Eod)J&Kn27V4wF8I zFOTG^)>B(Lr)InKYs7e@Z^aCf#>2&ALE`#>6@?GtH^zZbn$y;;mgas1Ce6TxWbeZ= zFWxhb!_gu+v)-ltqrUuhj3q%Rez<;uWk-ea)M!>>1wG_T_G+p?$i6bin>aKhfdY`cOSsM3ZjG(Ugp1 zF$La35yu$G!h+hgi|Ck zCmp^#UR+oFP*FEqzb4a4yKMQPxvplRHl0nd$ZF1jO@`Y51CX#%kU`$fhgRQ`YoC|= z>}{Y#YnY=c{J?P#bidGm_Q#K0s7FEcuEaTSek{ohdQsu1qtP{!@YA*oJ+H@!>dWqI^{z}BRB~Q2F z+xVega&k!eX+*)~izQ1u__g5P225n|CfVr-Q$Ok7$@)8gbAHRTGY)!|25Y{iH~#b0 zGzR5c^UAWKPT=3=_&>H$q#3CDFb__;c|D=_=g(pKK;g&NTwyERze~>Vo=6C(hd;A< zPzL{N&n~y{Z959i(z*V9T5^6yCe4fPtpD2c$YYTrwwj}e!D}$=KVQSxfGQV~w1sc~ z-t+$!{NGgjf4al|P9&b}cnTjyfE1q7!OI|-&Nz$`(vD#+Hp0<(G@MyO|_CAvVY56b! zkEn^0&iS`T!%|t=!cv=>=K05-dbXP2VHO~^r;OaqI&}Tl2ftPNHqeOY>wGrU1VV%e z#T7gK+ebJ|@aoE!Gfxh8D=@ZJuxaOz?<4a1GK6RkquXrxUpBy9K#rAG+xM4-SNj|H z-SECg72Yt!Jv(##?#ezJOKb)JhL9g`8+;o`o}HNY61i_J5r3&?JTee;gip@xs49dx z&B>gNIEI$k z;09|gwYrE8rhJmIuLO&&|hT!S(m)Id2i!q@-@eQmS2-%xJ>=GRn`g4#GsJ4)( z=N0oAIu1?|NsHDkHpZ~B_^bwpyLtuR>xOhpIZnqho*%8+o!(t+7lYC{Q zM)Qdm2cP3d%*K^(B*$fn4F{kw7Q&l|8kbLlOz}4<1ufPT*#U&smwQMcpAo|L#${l6 zz#nN(aZxMb-L*#VP{+ZnG>bcAK)@TaKQ&P7APso_KZ_AH?=XiraSx6=x4MpJRJnIu zfe4e?^q0q?>WE?!$?kpAdP~EO(@hJ$>B=>g1|R*eS@WH7tv}9P3xd6NceBKOg(1}) z$4kU~W+_Mxm>yQ1v%`<`GHj9JS*+RKmb43e)tV^@1 zUWfan$3kGr(kk2J1C!?IwC2|186j@T1Ru6Dd5+IH73K8SB&xS)4SeK<&Q==CC-x`F z?_s$Sm)-Gg2=Q5YW&5)Pg%qoPW-SO+)RltZ8Z$5#K|MU0eKGB_lgIgXz2bQR|N zk?qANf@{Xy{}}-&M;(FbK&NO&33?WD8pw5br=p-~*pXcj}EZqV^! zQ{&HP0^M9Bt4lznE%K{J5`zo&F@z@+VqK&#GytA~XjZH2e2DrXI=9$w*g%KdT=0yU zL_ba{eg!E2vfDB?lrUQ8(%;%JQM`!?wLTb$Aw)&SFYk9Z)+K$6TwIp|>Akq7J*oH6 zy|_x0(Ksi)_UlM^`wDvLdqGahLl1G}FI+yRW>Tm|T!T^gYs~ySo7jWe)4_(;d>(yb zH*h-FK5DG7KsT;1*qH3U3vYH~9n z)A{n8{}s|^~xFmc0|gJ-aKYpHdly>Q9G zTmI;4YR{8IQlH+q2NnBZ`7nED@)u%yn{r1z9Si(2S514 z_M=}0`9(l_Lr*cAWRYj4yScICpFJhQjnQvt4<)uW9`W1s7-Pm zdMj^#A=+OdaWFBH%gS*Zs%eNtbXpj0g4$9{Nkr5i=1}iu*c?o5uFz7xc6`!6*a-pU zuV)|h6Qm3(DkJX~2>mM8wGTV@IVm6Km>O)9Qq`B-X$mzSTRLg0CvJa7oD?an?;~yW zl^AjAb<&a?ln*;autz)K#Dgmw7?+Mh_YgVx+o{PT{gbJ7GyMNRUyqolYK+qC@0vW; zuTdOI_}aTTG;yCo9QG53o0OI+pYxm6dXMbVMdB{T(3|iecB=_TGZz-$PwqQf#lCY$ zpkfSrqHrr_dfks%xHSY=Dj@qR1 zB{(=7|7@PnRtiCKS8XTxf4R{l_Q^gBJWW+d0J#&xJ>udh>`1M5=hi5OzU28D*B}TQ zU6Rp|Ru+N$2L!}V_AAU(w&??>5$Z=20bvqz*-+tv_`@pmRgg3B^oX-Br)%CI{U3vz z#<(KO_8kT?X@=*n-4c(07+A#Sdb2Ppt4jP};Usk^p1VSl+YyE{#gB$bkF=_)4-;u* z{y3HU1e&f8%Hw(d`PQGHG8>GX-QKL?k{?)EKjOjF>)Yp`2eo<*9Z|=&DV~ABvpW+FJyEof+lXmNtfUD%<`6Nq|jpavUK;oxB$<@n=FZ?A)2?5f| zJ2B>6NyNN}oF4=#U(H3c7nbZNH9pWS#^RpZmBlOmgFzi78(qIi*p(|}lm|fX2i|)Z z2bABD*p_x77l5>%X-ik)o+=+CuP>(sdfu-!Qy6$M{R_LJe2CSBlG&sq7c#2#h=7`6 zb#MG*TKc)61FOSiwO#y$<`?_GF1cK{(VKe8$_*spZk;!lWq>E$c1) zIqkIk^Ra5z!r8&cs`9ikbXo&<{hgL1pr+3DJT7{sX1!8efv-5d?np^Leq;X3Rov^$ zP9mjLwPjD#-MllF?tLCViQ2n%L(Ds*H0upqZ8#5*A>A?O(=;SPPT*|VGu`@L9I<8o z)TIDtPg$f!C)AhjYlcA`$Z7Ov%gr@yi{PS@ZY-IWh}z!C&HmWhe{fdDVBR3pY9p`ntAaSBl>2R z&My8TLK_y8|7lI;42HB1ADZye1R$>t1N@nZehXvaac)QKzH_$C9%1pGV)fCO0S}UQ z(D$Q;kI+8=WuyL|6;`i?(*_YYm359VmIt$$aLM{30s=@&0F1zaRa4>?m5b!eY)}_q z)4qXdl$7MQ##e!9Bk%M-`iCTmG~xp8rTtSC%zr4yKmDqAEHLFhhkxw$pAPesAkcII zNoak&dh<^Y*^dPSi~Fc#EQ|ABd!7K?>BTysREdA>`#{CdD2=5CD)XT68&Kj!*-GU$Ke^!K3M{}jmo6v+Qi>y(nfVk4jN7b@_2*nN>D*dO~$c4}U3ZqsSsEDn}7FY`Im8 zaVi_$bF}=l!?7=ot2=9`%uYOC7acWMqpk+BWQwk=iHVwQG?3Z~x83VPzM&u5ufz2bYEbzs9exI@sQ!|E?$W&U`Q= z(j-%YL&7?=4-~ngLm>^d`Q38L71%$Ybt* z5*gQr)hY#d-i}j6%*5VbGxvUqeYs$%G9XK*hOH`eIrx<54VYD%%}{8S83L{?V@6Fey1M+6b>PXBhA3Gf5d%lfjLmD9S z$C`>1{7|BP84oi~lSizX8j`)!mo@IDfx!5-i{hhKDCrOC6k4>FU~&xx80HdCeWf;4 z8xsiCcwEL*c!DJP+1}2fXy#!jooi6+1;UU6NcoY{{kJM=L;@$t3`CcME?JT~EWrN1tNF$Vw5BiQir?KT9 z-3OxD0Oz)k|I|VIbmglxhN8}$Xjv^AW%4v%J`3QlBqYAR2$}}S>n*#L(dtrFbITg4Axn>d7a^t)n{*q?&wAipur3G zaCWZ}QFrmX8zV8Zz@P#_plCj^eC)jIcooLC_s%cGslUF*YIRgoqcw7*<`|AqZhymGBZtzwCjLzL?VgJd(g)58uetgn5VE?zdv~ z&m-uDJ_omz3U*g|i5HJ^Pu*~C>YVcW#vnS47qV^DUrzD zSsh$;EV0;coycBDtFtQ2bE~&ZK>E_^iDX`+&SjDouIz#|mfraO(WABbL0kMH#r*OL zI^<~F`b@J;X-Nl+sd*5|JlGbz>F3G2iJ`4rj#*ew&VL;g4Nlt}) z`81U};rbnh7j%o0@|p6GRsSw&KGYce+Qc%2J;@@1_)BirCUq=);1nxkdRAdAkxSJSgO-r)&478Bht8yx<{Q_&|?YJ3f6dq%NwG{ z`Y4SS5GdYf%A)WpwF+8gej|gpY#(#Bd zef@OBV^Z0`Oe42cc&`i;p8vxaLC0xdC=1O_KX9RPmEOcdqoXD77%t-duFibLCuhL( zK^8-OzC>ScxrhwLTO4n;jJXAwX%22?0-Mg}J6xpg!2TNBd(_Dcm~_cXT=H&XF0j

4_6Lk;}v`0T&?KAW4kPyvGoWC-~+JWRz;uo_S(B&+{oB&y& zr<=cVIXVhz6-|-#LZ*l+nOeciH3kag6E=$5cd*CF)`?acq2$zle(M+vLxs<96&)6j zUAj@uvHDk(Ud-df(eZz9J%IK0_xD|3PlTY_6@C$ER79=L-H~j!h*!-%Z(PH(d?FKsJp)bh||c!Zj+tjksf@VX-5C9^OZ-c4u}4JX@wr0HBn4R zM2<4Y<=O#;JfUNCntm;k!8OI3zMavVQnDRsjmxQn6j+9yW`^7uhu=WOnvE728~fmt z`>k}412qb%ZY-8>;n8${@1<$l`El6&E78MplM<2S5n|teQB~KeZJrMz;sjh8->@a* z{q}>Xn>@ii^n-x-%ND*}6-WC7n6~g@g_`oUX@FHqB#k6D3s$Ufm_$+h6Yc>;KVS6~ zoBrZ?mB&W%f#s+nq)PogANL4?Q{jWuGfmsgZ<6ML1k-F~u&Gy97u%cQE{zl=9=85kjZb$Ql8CMM zxh2}H=8Af$dqs2rZ#Oe4KIn>3v1a4^Fv)i%C7E^la-BQ<_I~Siku|I$GRWEv7tUOTqQE$)?u$!vR<;A&laipH&;xaD#hK0 z?2a$Ww)4jeL5OBHVY~FZK07tj46z#{o+R!(B|l@zfA6A*Evh>P+(K6WqzUcR15pcg>OHFxY7 z>Z4_tGFrl3`i%zUnUk&#sxK*TpC9Z=zG?+?qUqs>D0;k-(J|zJiMIt9eI-=*oRp}s z{ARi;3D#Z?_U?1QgFyGKop>>#E<8~I6fd%ry){aU@d&J@o(d50-)wk2$X`%^{a9HH zgfR#orPd|3m0zHBzmJM#szn`5o#tI#stU#6#6kC>Ha_Z6a|&R{YjaDH(I)y_pL=$-}fmKvk1L777YwJvR`~EnG4TT zE+S6wN>X+eAfa#6kz!E$(?a&Ax6uf-L(L0sRWkL_I6}!BXed zeWYo7p>9d0ASWPs2^gHM2fz`_&QAW7D)4A7*C#e$s;35m<(cqIn#C;9T1Ms?tt4L$ zC2Aux!O7d~)`-4)rlm>w@V*}Ry&g|eslJ|&V5ic^JN0DCL`U{o8l>QpO<&a=*|lF- zH_+K1ifqskE>|7PO;n7p9;;O(7@6d}p{Mfa#Gd7W(+z(%cjlq2n6D{ACqww9z63l#gNlKxqo zk;&~>^rZec*y)U|4@Tt}i-*o+6(|-DK1L;u%TtD_UHjmJv zBoi>L6LlEu#t@=6h23XnvI$2+82M4|sfZUonjcX2`20)pORfte22^;~M z?SeT|>`y+mf|uOlVWPY?xAVObLQHge-O9-P8si{rcA?!BNB5~|I)R!SAIqbZLyGGj zx-cGl0;?%L49E6YyM=!u9rbMPE#Z_mHtahe4|*FAewy2FV}J7)t_XAQc=RN8-~Q*Z=UXR@u6OVqRsHrm<>lK-uvtrb3cj(SYEw71O8C0a z&;sSc4*jJ5j;PZv)`Qe(YyAl!;lb0(rot9fOIae(+p++Vb zHnEcc09a=$mKSe4)=&{%|BK^We1h7ARz*m_i+7`GeW0W1$;!+La5xO`n;jIS@6^E^ zCi7iJ$1GRBlUsGlG?ew_A6XmmfrA{FYO>U^XX@-NF&`m3?#}@x(hAuB40+xN(2a?T z{*if)pJvV!etTCQRQBE0oyaAv(Tw<7OTpDN=1HF+L)FZ*vOa4Q%PCD5&V|p(moK10 zQU%tp1CQ2vSWS({q^joSZ~qnJasig$f_vqr0Y*|oB+wEFvrUetv{;+IY&w!C+RQ29 zchGgugJVYJ0vJiM?Ota{#KT0EDESoaH5^1F*R$b?6ti-}XPu$=`|I7ZvZy zb#{^xfyL=89n4gJEq?oEI$Y5vGA(4H)Zv{m)Ty7qJC#<2tJTdjenN#ION+YqYf<7z zi8?<|dm|rJYcQ^?KyPoq+6!04{V|{%Bs@j!w=s0QYh;=hfwOQ4A^g$N!RZN67Y{Q+ zhAD>L(n{L{Z&Nb9A)CvhB{Ae*gJ+;Y2j$O?^EBy&Sx4DsRW!UQcDnVo2PjD|E5z3> z_{m#U=D%Dkf00t<5JI67osdM;hgpxQn39=()Nr@$?8>H#&XM+}N|-EZAz6Ej(w^9) z1JJL;2GLH9UVnvk2mn`EqH+4KXV-}AVQZz4!`J)9^cs|P^MH?hXQtZx7GG|q^rcb7 z@+)Yp4HKdCIL~Nyum^+;%xygpMD>@zUV? z^(wzcp?CSC?qP@JMW9x+?h#PzBh?&^{RCaXuqQ8wHY^H3sWI05_#+_7~BWmw^B<;MW>oD~jJ&Oy7%C zFiRd*KL#xMLFQRmMNG_Te0lutKz48O8f!i+IoFhBh5>F!>7BzXB0EU{n79kfg?kAz zg>PG4A`{lCsqqOP+W_r_3&?gWxo+VjH#l#EcS^vd=~v^cXE!fgBGX*MH{sA5IG4Ey zY72ns7lujcH>+&KivWoS;K+x5s1R?Ca>Fs3RUM@1-YE&N|G=57qnm0ZQ%2VnjZ z<<#L7rGYS&xgM*7laYlfaw~n^x9E=dZk5-Mwv;0e!9}fF;J|eKF5@5;d5cq6M^>v= z8y(&yd4Nz_0Muap;Z;D8kJc!e-OgbLNMne(N&|ZCbg!?T&j$c~=BEHo6H!X6BL$Z# zCa!5Ai&8+6$Gxd3j~WCmNmhjlt|mAFJO-0Z*W;7RZzl6A#CQ&qvoQfc>wVmA{qcQ| z9{o?J38iN~w?NAhEQ6V>fQv>UUYs8_k~zd8>y*tsZVQYxk}9JB%kEECRUfl) zB2{`etgDw>iC#;c0>F(K{*&=u$}mdK6x`m!dB04i(x$H{vQ=Yzkv zNCw~{1qcuHa%dJTOZ@yvTulshahRM1L(zS;a8BP%r|!+Pdtb$Q-t~y2JxNr_ZyDTo zR3lCIGF_{5jN!cuZ(z;71_F=HU*fGEp=J~CI!^i7vP>|O`Hnx;z3&iJSF@KfD|)=Y z7Gu|5{zQY7O{N`n=guX$1bI4x=VUe=zbHyH1Xbz){<1VN}1wapgJv**e3>S1ZMo z9rn3)nfwq8rvvdIX#mQiEf5M0S6hC3_Ta13pjU;)-s;m^rt|7u5g>$(Y@h6%n4BO@ z+0uhXZ=^**jCdhSG^CHG)nD+jf5+B9yX+|;Qaa*eyi~^MJ+wo<3swE=Uh6J>-=>?k zs3ePg;b6~))69BnxhNsIWz8-g;ftsNw=7JZczyIK==LXS69GZ_30s1vy4;F)vOV6} zj*tsdCo|b)Qv4}5;edZ9u{X_tiuD>R@1#U)UXc6$*n7{YrrMxwR60mgQRziNiZp@H z5rRl3bm>K<_ue}wh(JI(1f=)gdlLl&=^;pf03y9f7eYBZ`aI9~e&6r2&RJ)z^Cz;9 z?0xUqGjq>ebIsgpCp(Xc2KDtp>P;tTk5#qakE8|bt2&yEMz3*-DWgr84&}Mya0(dB z@EM}I^d6N(zqG3BgcNKPKq?-8J}zNqS$n#lQ2yIn_B$FB*Lu`pQTrqBp5{^0LmOw! z%jz?cNN^?LF9M6#PlppXKF_t^cZY+24AR$KO{Ydi3chcO`|p zB`tNghX3;56L0N$%p-(uzZ&WDE1}mR+w+aO21b*g_)MFL#bkX~1M0qMBrH|Vj@rER zIFWHPzupbd^p?xHywgwkGx|o5IxA`_2Plj2OtxZ0PiqDS9x#8=8ZXofg|>c$QbOjB ztkqk5Uz!(GGMD6W6jhl%6x)n{0|s2U*fHqLIO`BQwo<m2oKbL+^8Wd|PA(dU4N=RDukMkZR0Mrc6pJ^(p*IIBFUJpNa>Y%g09@;EQu3{VJQ#&4Y)Z;h!`0;ZHCxpsA{iE($4m-L z?gXGK0E$edm{(!Gt)TjHQVq7!-T7Ny&_HeeiFhN^Vz?-6h2fp^xhKMnYxWrwhMmqy z>IDBS(zv4{@7_)xb%Tf47j*nkRf?U{wv*~HVh=#dV$I~AvH{K@kpYX84v7;(quuWO zN!`aAB|^;?lpAfzH*^6wRuO?;)>BG*W=ahOJyxZ7CX50GjSe1H*O7!Gg{=ha7$odh zC4ffnosbI3bPHg&A$F8XdD;+rKyzh&R-I7a+Gjn+A|*CMwIWy1*kE0}-(eGFPbo}0 z%7M~7ErNtvY%b%+(+zYOkaF-9Zz*m1aBi^6)8k>PNeB)}Ep}(C**ctpE$hBOEqQ`Y zV9rm3J*mW|f;{K3?RV67g~FQSBtUAc)xIxkB+f?rfn}+rhGN3XYoMZVhr;rk-etYZ>VM&J{3VXKq7iJmQOQQ2DV-sr*WK zO+)`i;0W`gJl0kAky%w_OI|%bfsDLBU+b$FsAqfgp2xvS+au;Rs+ClvSB}|myhbDt zXhxsCVysA|ADGRNQ=|?_pks=Ah;@aId#LvwvzPjF0b{rOGv%jlE2MxM3a9d4@4P3H zs#I+fj3 zqJhc-WF|q2(3>pHloQ=NvXA$}KITl0R^mb}wKJHkQ&`#S(TPmeAYO%-k=;^=xN#kz z5W)YaFZ6;rZawSx%I1Nf?2V9oyum~c<_Oc&?~FWCv)U;%vX6p9WJ^`vUfcw{?5lh( z==70fhg5R@@wo4!_&Xj7KlSIrf+;bI6V-9Yh>i5>kw7|MbA7y%Y91W{d3!&VsE>!D z95nt;v1!52E;UZC&z&yml?&F7>}VQ~0h5||&>h4$fyurBtq8(Y&E%ytlyUiaoY(hg z6a%AdU8b0;UhCTgXbm;*ivAcZbcxM{kB1Lm!?`NgtUKxh_gNge#NuuZcwk=4E^Rh# z^SbI}P98PvzDJB5C{K&HLIWyBI;(Wfm05`Kv~I{okB_6DaknRP`~*e#SD$UeKIv@< z_xTG@-*#3EzLsppRm7xQ!J~LT(8{xGF$L#7pc>$oz zsqc6o`;IiE4!^T%q~B~Jg$ZYSRI8jvp5M|4#r9A%Pr)< zi}}4LGx6rn{E6Qt!CmPA3vWR;LXL>E%k>poB_?HW`rT7XBkX#5V7RKB@*p#Rgs-Mz ziVBHMit9=1Sr6YZ5kJx#^XjNPyJIX}wm48Q-WiTegEZNw@{hejY5vuFS>n zV;*B!KP?>=>rfKr}zm}JO_3O%-eIXdDJja}x6uNKjnJL96U2iJ%J!dAS4 zOwB5VUG`1xlW}NB6rkq#7&XT?XTWSb-g?;?5nFOo0Gb(wy;7Q(Ak@^D%$h>STGna8 z4GToFvli_Ba=-J$;8bzS5^aEU91_hu-|q31)u$MzGeRM`xzPA{GEaO-cnT z9(%-A!JOlOZRLWwRJkd~*!=PvY3=#!RBgb9=S*8bl{R}Z)0E+h6yH9#QqVgGP*H~w z^!C6#tSxe&gXauJrzz;a*l9mgJ8}BHOeLNpfo+}H%e%wC$y&%4sQR-1;U!bE?xJ4=bY76PmSdTHWhkRSnRm_579?L z(@t=)bpt4d(NJE5PcKN$Ar!LDX{k6GzVq!vWb4e(3dE19KjJHpVtm7PKgtW-Q)l*m zz8pS3Yrfy;5pp}mw+iabvQ7nSTOAy&;*keA@!aF&hduCY7!YL(I?c@RM^Z!6elzCN z*)(ISe!q19G}oyO`ui(K}ob z8+WU((ACa8&-9x-A!3Zfb$<#uMnI~&&d>hkPbJ9P5U?zP1u0Bi!f5?uZ^ugt6zE>@ z`KV%;O08likFKKITH-X}G#jWD>F$ml-=~C0%V!!?lmIZuszEdc6p`n&r1en}N)q*6 z>$$eWqX$w329T0wm2LNjY_mumnCA{5DB0P3o1M2RgKj1#%>Qhrh`J1i{7(s5!34

PfLizIL|LA?9~snQEq+fJxB*Ng2$S>ula>CY6#_-;Sl zRc!0N!m$ZrA^(#OI_qYaw|&w0m*T^8D-Tf3K0;X`+_~F8z}l7d+rDJJ#3M0p*S10m zVGz43F~wN$E%T}2$qw^C(gq4y=N0=cb!qtaY+Bg4_wpC+6}UuX6FFlYrx--VuzudEd;~!As(M{3uPr<=JO{u0MhvAOUna z&E+Heqd#)N#`DfrVJfuJ0=|==r7ATkP_$^yBcWbt&>?riZ8{?AojHPOPAs^o2WK_4=mq`)DHQbv5YKU$~hzug5Dt+(n@%12djTzLvcIM{eer zs^T|M-9(@Y1H|AV*b?fjw4rg z5>i%>0s;diOLnyRSn*}vz;YVXkneOf&P&_&Kt8}S?5v#{#@h1<1B+mhg0A*PRBNp^ zI(jW`W5>L*(Zi^udHsz5A)w>)iQ;Ns5=M{6ML3*DH+ktD zZ781b8l$Yo9Ou@USAN`IQ)nnnnv@JKS_!ZlBR*KQ;}uzfQ1;t%M&Q#2@m$B2w>E|+ zitL>KNw48(G!Zo-1i;u#3Z5`cmE=EA?|mX#`J!bM`wxXf80Hd zZv%&U(>u0|6UEzb_q!|7+#n@Ga+}nGjNenLlX*-cQw&gfv9@&8(ZZ3G)mbre@*rp7 z%8)))W8dbzG%!=fjgUTP{%;tHqpLh%f4doW&_ANR5%T8dEo+kDl1m23d8cfnsn$&E z;67H;_yQSjc|&n^oNQG7N(X?_?v>Z4BD$xAvtpcWO?`<${+}aIO)6|)ra3E_u(k7< zY_(BbCaW{nqfYST2A9-VItC0lA_I75a+cbEi;3C5GQhFwdwpu(%7~Ix8|Zjje$8Pw z;0Q7qC83s8frrdi(MNQesnUs&PCYK~>Z7X`*?qI(qayDgGdAEYG z&RxPMV3q&*AcVib8(HHB*Nk7w=0yu~v$)?8u5hN2>Qj02!pmq;2Lbc8ptiJ;(_@vd zS36f1?Y28&^cS+QEWZSN>M7;0=KRk@#Z3qX(kpxGxC|z<9~pH4%5#*pmn3ffnESZ{ zm_gp*!+&PAi*k44SlAbt;+RLJ96f=AjC$B@+C15*iC}O}C$Ka1{%V_B+QYK5H3kDY zr+f?WJzen+0mDdXoaVHh&}Xl&h<2|R(OqyDBF1N_1fRf zhI5Y6b;I7R99VO35MZ}g6$5`+PT-N7zrbnDT3PQ6pi8y_g@yv=r2P2g+IbUVzloU{ z*>(li<~5s<(Hkc-zmn57j_ww#*W}KP@AJ&m9id(kHwU0SsgE|550>BTzdEc2R0*(5 zG~h5T7erUy{bdX&6yG3EBwBG7O8#a54e<5iR#$#9AL_Lf8Vy>4Tv!b|en3X+gib#v zGV(l2!lj{j`_&VZw{l_TtXQxm_WnKchiM#R?*^bCzsT~H;s)w40-fHTC2z%1+M_k2vZyd_7}Gw?4k;P#dqtS zJ|Mr;4gU&jJ5dur$tm3HIA?B`34(R?CelO22&27ltwQ$D#*y zcH)J=s;@j}+Kuc4Y`ZdhaByuPx`I~bd-js(uTqUM*Y+eW6xcBj^iwe+1`rKFTDMJi zx9Y#X<2i3Od7_0Eui;Bs26p1O&w|X!faK+z5jRZ4aO3O!4caDXabK{=r9!vze%M*q z3Y@=ws6~fu8g$lD-@HB%%#tmX!hKs?j0 zjnvqcu-kiIS0UFThNew{741hq4oUD6aP5RJk7Awnpw`OPdXb!`d_bXBbv4+(N#g~{N6#TKRoxUs#L@Pibaq;0W( z)&7JO1B!)kvPxEWqY}sK^9LCmF=({9+njAxk=B~^liB}Opj)9a{)B&j1ZS&b0b+Zk z6EZp5zo_HBw5<+cRDZ8Cl0kh zLCabffQT?=dmo@Ae*2Lyl(SOU-8ek!de|*+Q7r029f<9g|64-+{%|DOi=px4=P(v2 zzySsrvQ!zsc)ai0bHBrvZm{1N^D_U?o$p-)fzjQr?swm463A9>|L0_?fAE%~w6JfF zLW1sxGf+QCA>rTG6Lm`SMz9NDmrztU(238v=Cw`clCj#vjTh=tEq^}VVVmF) z+Ag~``Vqh%LUQl0zi3IU+tN%@ZkA<#;Y03)R81^TO{uah+w{C&MaZHJ`0Qt*GgR2J zyw$HpA7I~vzQGU z_oIz}(Z4l_u+eFr$F~#1HvzK}KKZT8n(=7x&&aemMQj>%uIcJ>Ept-VWIH{%_mAJL zSKLlh3{Moi1pGqbf@scj!q42*fhWFcuA|x|L23aUElz>=&YOk5;oo`uYp-;ef9r#O z?phS4u8?1MBdja`a3;t8kM*Q6y$pLVl&yF2tNn(VSW>|5;@O5a-C%qV;1~sd|I~2D zeMnTE)~M5Icbus$(wV!hW#u&EF>3tKVzku)3C}jV-HW_m;TE%hZaMQfyvgE0Hb&om-mOSgxNO@}oSH0Ubw>|%={+D` zKr`penww$d)=C7(ZCyaK04_OEy&a<20!NK(9RwkU>OG>&X9_*|9d>fZIQjmtCYj3c z;;HrOdwLrt5hCG23eBEvo6;aqES9i|K?c(-7%8UwP_2FO(q~3S7dLjNTt?9=C>4YDGJro-9tQ|7} zqgJ(>)=1^E^$5UhOMgcLg1^iqWvNVS)qarC@Qf;9Uo>hG|JsY7XPg_r}G5JfB4l0GCSR%EszXv%Hl|U#Rbqlt3h6dC-!- z4LlHCh_iGbk(BBOAetHY@n|T%ZkgnJDZ-qD3}pMn_TlVnd;u4@dQhEGB^cL}(pcvq}9T^Fxjq^}r*MY6b2uPsYBD7y#R&zMrk=6Wm+X z*s7%c5msreQxB8WSQf2~y)l>=ismSkE-fn%cUc8 z1(bH>y+K0ZJ|}OW>$HkbBe+-`AiBx_rw$CZB-9o!D_vI@uK~rFhzC?ygEe4a3cYX9 zhY*egPT)Bqu*wtHS77Jb?)Tp?IsGkm zeymxER^ZuLkjN-p%Bb!7nq?Bqi+#ZM{`JFPFg7k|@w;8&UBB#9ZCf2aELw94jK*3L zllTSIQ`p17d8!EqczFV5OcjcbP0N|Z`055UI0zAkrku_=m$GUuJU9H}I5nGW} z6hSbv+>H?XyCP+_0W-BNN}tFfuxIiK$qoiv&O7fP=Gi5?B@3OMj6YaR1VTs!8oOU9m2GTv_C8S`*F7bTr&9R zz3VA;ozv{~>lu8D{LQo%0V%V9oNg=^uE6}OdpUA19$%RLu)waK`@w%Fj`+s`Tj60A&$ACLHChp!zAUzNLyIU;EG@lA}P1x z9`zJ-0B8~seUKeeS4%qd6=`Fa!P*h+nGu9^*!f}&lb`J|wr0q5`g#{T5>x!P)@TIk zpwy25v)d>PH|wn=&Bqp`6WE;7&;wp*H|MXfgVg+ij<4k=Dqo-5*Z1_vt&kt>7LIL% zTO1zpF4X6YU6^BZtR4Hhl1r~m6-~{xfkwXIv7R_|uS|H%MZWWfZ!#HtV9+VDFM9sVO-CPCgVa+VL>`Z5Tq1#%UPvvQpN?dZ71He9tJAqjg{lyW~%V% z8WfM=Q^YIF76Rs9x_m;OI|v2nO!bD^f_UO}*8*V^2ekmfdH^WYh>ZAdv#kZU_cWJU zX}heRk%DbkL!}k0C`PXBnGQ_rqzrL1!{Qn_n(!Y9ez}uczkt z7-{gvf;KU-l*sepU}7{=9zJCXRDbnIs>Kr7`uuK!1F*{Xm3% zL1>QcSyp1%Q}o{!)&DVaqI+Y=_w=kCuhNwm*^`uacG`6~Ui8gG2$h+1`ea&jE1X-a zu#3~m+z4UE1>H!~C3elE4TeuO+|MU{hLpVR5!fzB?LixK%qR#kwCVqr+*w%E`ZKfy%T7(x-tZjQWn7%5KRg5sL(C!;fGuZ3*TuX-`Rdx+OA ztuVhk*K=k_K3$c&qcy(DRf45F2^T|}!*mEviArYm#myRSsYmN1UJ(9IkmWu2C3 z+rQH}X1fdSe{~cd`B4M(CYV=)_eprfWidOy=0m{2l$=5Yx;iT%U$4Fs0h+QXzgQuC zSMPCT=%QNc`85!u``Flx_>V&RutDFzOl&}my86>* zEgM*rL5PEDw@YdHwM9UESv;n)sE`U?L;JSQmg+Rr5(%N$mn9iFHtx<%-dKlKbua;` zsK?OYb5~sWIc!u;!MUQZ2u-RoLN)^s7yg11{}ZZs3@q3~%HakkZf55Xd9@dtTxQUe z0E8eeL)p^uq`NjdjkzRZo1JTypdO-N+og0N(OiY0Wo(P3=hY2QKQA#$CIVSlp-%_i z(!Y~p-#-tQq>Xx$nfb@NVeVN&1b{nVa1(q*dfNLLe7oo0Asn|R+h7>TN6cemX+-Vh zjnMLxL3B^U)_q=mr7_N)-!$fMWONUL#vg%NGtzPas&no`D_15TYScv4-IC@M?Mt&DfOXe$Sd_MFY^ZJ7UqEg@^bPPhxhSXGCLGX6*M? z|C0kXd$!=shEX~Cj4!iUd5HIuKKn;&#UUztLfoU1mnN>*X(vys_KB<~y-BGErp@83 zlbNj&sZ)?I%L!%2@%3Y1v3LWM(V*>wM?1`s6SJ;bZ1PtIE?9QlC%sHOHr8*Z}5 zImE(nxKBOcW$8_<`E@DU@_!HWkOg{FYu2}C1pd{nIQI~Ro=>0To{-(UP%5JFoVjBk z4L5Hs#%Wc&4z9)d=e`4&?Lgm4NtEfIUx2f-!Npf}g3iT|vM{t83FU=S6OCsYgMBpH zEu&u~0{``RDUbu-+;Cy|>x3~D3q!)PX(yf0j~mMttyl+L7OU4VTu2#;5*Hi`|%dJ0a)R7ePuB8>g@hR zt3w2PlgbIe+%2DMKv*{~zx=NySUt6y$AVLP%S4a$f2kayc@}VI!S4-gAN>Y^k?pv2 zixxgnna>fxV)fWGC#0V7(^X~_=ykVoL;E8 zBUv3Vhz9NqtA~A92&2^N(_s1 zy8A%vg*XG&Sy0)`Pozd>Lj@K)@D*u+_2IkXWeTRff@!KvA)fE=MrQtFDqsX=IB=`h zAl84nSwsA0YqW+P$7d;>DMEZOm#Zw9!$>`mb#iHh>hK?OD-#TZr%lfM)ca4UxJ1?! z67+m622+DrhVT%T`ocV+9#zkY>`ds#0GBAYo7;m3GWeo(@$E*Bx7q~sy?C~Q1I3mb zn#;8}SOxyqWdMqU3KJ{r-My_fodV2vKm*!+keHzqQqqMODzlxMra-xjK5!R(|}LVaVcuc-M>%4CO@BL)_faAal2dl zz#NwzeY9(k9qGky>tp#v?eEr_r23t4#()kA7?WLA zT%^jW)s?ROGNs)DcX4@d9L{v$s?*MSRcSj8Ru=No0&v!x{}HoT5BVsMmlr2AEk z8MvNc*9l4OPPQ3>D-?|RUI^kev>R{=z!wFU06x`V`I)%K#)tS1EjZ3T|A`^L24i}X zqIdsmg&w$zRdHN|)xMaWhd8H|d{~v7i+>iHg03J+vnG&|?g!4K)6fOJ$XgP=(mn#THx2ZpsdOm+sV(yp_QL)R%~1-lO~fCg^AGq zyijl}Kz~q45s()BPlvc}vvUQfKm+LR>sUJQ2UwJO>br)Eko@bem@r9Kg4Iy+e@yL+ z`(rmgHc82o@&m<%mjv{h^%D)O~UI2);T%v;R zKVI|y_WvcASL*9_@_(EM@Q*lj00a6qI5{Z&pF#gCDa_^nM^^E_UGwkX{NHlpcU9x` z|IPyVe{B)}|Be5e$NvNG`~PF2?yl7L(f;2o%jU6j_tn(Ta2;LXm>Kb)5E2G{5JWlM zv>e%Bt28vk>uPB~-w5%QyfD9dR`|#IU*8${jFwg*7*rC)%=uVT6(z{v#%{3HNeLsH zliFS?*K1Ka!dl(yx+a1};6+l1l+4uB&N}*}z)gd)IHRug;+tdY9s$9MVF>>5mSVws zwAjjQjah{M{!~+LRetV0c9Ztz4Bv_U2gEsRR$sEp%zHjb{jBN|3jda{ou-?XJuj$h z{9}8yjgi&zd9};+WofGgs&+*RIO^&ut~vm~5w{y>9Y+MZdj!ei!-fyRXRa0&JFOnM z3zw8!Ml4dQA9-fKkz?n7yM=iU%K^>7TmWd%gIfh(GT+MUeoL9bExh3EkYlHoCZyGR zy2)G@c-}D9-jbfB6v$q3X?W@MApBnSy2#|0|c0Y9}ABCfx={HUKSjSrE_wVV^IL zMvWeGxUT`LV1lGV^KUUa7t+<2F^)^`hCv zvGqbOmt)^HY_FNxt(4t5920DZXFho!1+K@Ap=Ds*>pD!&2pXXtuS^#>b820pp*}rR zI1Ain7tM7E%Mf+!A}H<3jz#;wF@s;H^SE<_jJ3~VrXE-Dl^#v`>QBj=Q3-5 zQpLk;?4_VPJwO!9FSnQlf#okmRkf~gw~b0pELQtY{2Y)Qk*EkbT*S%DL<-d)=G=ro zzNJWdtm6z-g3Z4ujT6p%8$o^e;6=GQ3T_LUqJflZ7HEE(eI<)q3t7y)Z$&ARziO57 zaNhUi1LXS3@FlgtdsQ&{`Nc|;s4A8%y-_a_k4~Av47Yj~%GhFXCS?#fWn<1q1tZrD zrfg#m5}M!9+Zv^bhIAeM{@1;+wa(+O_1n$UCX>{F1KEET_eCgov^LE9t31EIDDG&^ znEdcoEU-U$AL&tsfNrNcrU!LAd8r;UVI8DSj#~JdRyF(vD3=@g1a{lu9ju&_(At0V zh;Z(YRTH6JU90Uj@k>gcRO8U2bsT+q8YiG8r7stKE|TIivHN|2T1YeS>e#EvNUS-M z%yXh{c78fgRo~$?PdEKX~z2IO6M+t`V0(luQDY#U??h|9hFc&Du`$9;*nwqOR2X z4Ipw(wq^|Px)RbY93|QAEp^Gmch&2V$=FHc)a^SHJQfz98hpc8GytTgJB2tV9q6+Y zbR;m*)Km28JL{_Mw3ud*4k?G_)#PRL<*Hv%U7)dk7YqI5PeRfBksi&ub7@u598*Y4 zVpBLYE{67+tS#UvG~i^$P*tL{1TLQ6xa}l|hMW3XjhyRuo}Ca(`kb5xdo=99fWx)k zL9wkS71`)G4Yc5erh%EvL%QYlPG-KxGu?dADeTMKc(~qqeWb+)PfVVnT=S}LopFX?#x^pK)fQ5*BIiTOF4l+ym_%1^aV zP-7^sVTLx{PNf(B7~-%KP>_4Z*&xrcc7B%7g71IwWg06?Z=)-q1OK-or_KfSt@^sH zL;;+;>mF^YJLw}HhE;jKL1V}_3u#DU-yL#QvErUr$6I1`l)NU7-B!7sE3YB3w%c=7 zWR*T~_Oq>!Ral4-iuNq$;?)5QKdH~!(+zJlWJJ=V&4aw;p3ryidJdqH*r=X~Bp5=m zK4}>v!4|UG^`{1g>AynPOs)DL-$n|&Q!luwTA|pH5@3UW`+Z`2)@{F0g}%!fL;Q-B zlGEWZ^2%NKudJq*Io4UDQr8DYS{Ifb`-kh@o)M7v97V2~|ANU7;|{i?#|u#g%kuK0#HzGc z=gO6Kei0cjvEMpWwi%o}A@A!^#Bt^3>Ed~Li*oOTfYfK&|AE~u$;qAeUEt8NpM|qx zL*+AFB2ksi^=~E2pR-Ufu*g?Y$9bZ1Z99?6lAfLF8)&GP7dbU<+S#(+IE{Wfa=|4w`pmt2qt~@w zTZnw%LwqZP*WlTQ+G>)J^1I%rF<7qoHkE}NkwxgCe2&SL3Ta-Ey-dazs!ta#)c1=H zQD~{Xt~GNbiemyS?g;$rPX*UkXtmv~t9O{vQG#sH%XGf|A?F#KRj2vZ7wI57o-Krv z^IDbNRs(6_#$)Zh19*rtUSpu)_>suVjix()hmXMT7!m3n_6btvp$%kvOFRlDJRFZiPa|{rj2-z zNea#rh3QsJ)*Bbi>rYL^XpSF{;>e?SXBz72K~m1h{2nN0WyehC3XqGuzSVraEB^Cb zM_0^lvtwm0s_ORT0|~_4A;Dok$omz)@?^o}OW!KdJjrvFP9Z_Dy_PMZQ}GJN(>Ifb z@9>%OI`(|3E?YCzIev>fa`k7=#;gV2DmD5h!=#0KGq7wRFu^(sez%Iz(=`jGS|Rw@s_nb7S4Oq5Fs%IE#RPteE-~Wyec>g%}!P z_shYKNiT9;d4JF~i`+CHfS0aVJiv)bZ+XAbZ@Es31L5ww%eM-w=M_#&YwR-A=FTr-&tHLgqJ4`Vc>zv#4zdPLWhEZZpD|729*MoiiSgYwXoKKb zaO$iAbzSQ`DgV34@h%WJlUY7+bcv#!9^e#<;zFuF4VL3jYfvBJInYiqVl z-AgP>_^;_+;4daa2)5ZSKl+MfN9NHywOW096SOH7SXq&-rp3QEdM@g{SD~8$mMaG5 z?V5JsY&#y@OJ-Jvak*b;(|Ur=ICCA7Mk}L*DzMYN_P*E1ND4wOUU=x`Y@_Yu(cl`RQ$%TF?0>tB^#hI+w3q4BNIcX<*2swleC^ z(po91P2%LWVwbzW$jy^&o93vC)c-X zjT_rqvY^b`gVVtetZ$W+*qoF%&^8oib}-BV@DF z+n*IE%?kPGXj_!xeqt3?d7kZgn%OkCk`@nxMW zNMWXoo4!vE68%qBgguiFOCd8zUk3h$65XvvdKHJRosokXscT!g=8s%iF@e)W2%8`d zuepgnn{e9doJ=SD_Ji>C;rT-9y1;m271C@rh7CunuaG9s_|g7V;ebixZ4VXGgcK9^ zj|oM#Z_@eBpA_{kL1^d#_FE)fkt3`>&KJudoei;qmy_@fs3*W}fE_$?3l2V&`mNXM zzO^GUH|x6TGah)WVUG%8KrCSNewAvO{>79K<3d1_@%+m?p-lhfidReT`~87j0vx7= zWXppXl#LgTGX`wp(>Amb?!>BBy;{=buZ-lpHhP3S64IWq%L2y1ga|>V$R)acrWkde zUA7@U04*b;V_$&@QfpZdS2HwpS78=euZ9B^GWMFJS7q!xx%?fel@a)`s`KEE6{2NW zjnYBPv8qETtJ9YI;E>nm-WXhe?>0>^e6>YrJr=I`ed!aj9>3YtHxZcsKAkuoCj|nun!&y|UWvnIWxr*tkzfpCJ(lUuK^H za64S26~F$`fWJdw^&I# zMEUXkuNx*DNVExP!DU%A{5gson=D`aP6C~7*L^{kITSAs zg*wK_61#rk?zh9>A-bW!PSI|89F~FP6{?bCL4xx#i@LbTgy~ObwXer62zbOtJAKi= zYY*!c-w9Wconz+=wPzYp^4`Cyz4y4eki3+)Q{wylTm0u8f<|+A9O=L3Nq_>5dAs7! z@14`L%QzeA-U1boK4%LnN$eS>>}f+MhduIs|6K+|J#`(<`J?vVp>A^)Ijr#k5RwKp z`uZ=03c~}S;`7hU^BK~;pf3C&F?U}kCiV3Wl%0diC9&!#In__xJ3ANWAG0$ui>iE` z_M!J3Qt(d4^&~zWk5^LclwIjfxAk}AGHU{MdtM&vDa~&^px!nRFohLpKS2|m4x(xu zMfuIl9E6taSmVU(stD{)_q8K)P(C?@2_8X}Hla(XPCm4x@RLn&o+nN=H@W?U4%YOX zb6NQUI7ZIy6DRgQ;kHHKQ4`L^h9@F5#57cEh?Cdzihi?8c5h*4$0=^h6H!0y@w%fB zZx1l=nn+|7!lb5jbSo2q1Gg341`MGe zD-mcZ8xtJWmW*nRQvU84L`1_S42GU+8+5)d5J<15r}~Cw)+@f~IlP+F=VbE^tl#r+ z|Fsa9mC}?>l&=G1>Ln}sO{&~hD8*rDlCJ4CXG){}GQ6<<6219pUggOF(WI`PGqOk} zutIf5OEUp#?T~;#z(*DMHQP_u!Xz%cZO-cNfkBEmOeoeP1A&9|EvE-#RHP4mZnHms z8MaaT3`RP;rM#kak654Cxn_s;*;*gqh38)X_FC%fXZ7ns-TC6TaOgNBPQS&MzZtm5 z%{fV!pMdeYVzh~PAyjzv*X5>98(z2zAhX_|bp75TrvS!rQS&*>%(ir#2;MU)8+X)3 z@7F2f_|iDn2c*uN$)1(2AS8#jvx$-(%Q3uaVIQK|>U3M8GHZDC6!*@fY;u3AET7Ug zQJ}LTpgaPY%Gyp&X)(0TT$JIRF-h(KE@bH(j~D_{cfNpi5ZPk)RON~Nyz)7l>)Zh8nOhDL@|k#MNHX;##C^)wdl$w4rX4jb&S zAb`s^7Gfu9%NX-vVspwa<4Xg1<&x!O`gxA6U@P^H)L+TG$>_tYkA-RH^v_@e{Rxhp z6#^lB-Z%6qLvCRs4_0EoDCZ+awg$1#y4-PF&^89=un(=jpoX)9`~5;3aif02?^e@w zUdm{UD0~T|a9N0Q*8?`O(p`cNgCqR7?}1}L%rrLZoN({3JlQHT&0q0oVzIV6gY65Q zPHAxV7A#tzjz3i-Y@VUUwJwEBl|87J=S_L&%-ZG3W5E*GRvEsp!IP8k@11ai2n%q& ztY_<%C?VZPW+OiC>IOxBKUhc(cI5k%R0N#MeW15TFc+f5s+Q(>o)#ct?%9E`TL?-I z-NQlMt6$KCO>A6clOL!_%$rsU4c}@AQi32CYVc{B?1{E_9~x!#pER@XTxje`sd4M< z3C^r4!^@`m;sbv1w|#7Cba4KtH&`6NYdU>G)+9Bm*VrQl(E8P1E-{|<^occ)^TaI` znFbQ4QkIr9SiU<>zhAvih01^3wU0WzDPFD@`{2cC*r&l!SKRkXOxLrf8cD&I09_Ax z(_6k{Qu>Dbk>*a@W04np%r3}$V0={>e`dVr_Vx~JGgd0OP;38==KZdIy>{=KH`}o> z7)~hVy$J#{c8ZFbVnMp;bmY`mRpi1}gJty%*nO^x-e~^IRIN7Ss(FL9wyh5O75~Y0 zaqS6K1kOjlIiMm>u@7_VGW_HbRr7M3QFAX2IZ{}fO0%;R?==&e$?O#|1lv6GwFh>T zgr}t|c(e`zhq9K7Aeejb+p?;zsm_?+=aZvn?7yVg{NSLf)>5IVO0bomXwx zM8i~Od;(3)IttI`1DpN7qMX`KJDgWeMq-btG(Dt#B`4Y7PAD>6u;XrMD-8m(!(|`d zx={Dtohgdo>i4l)lWOKUH>;Ukcrg$y3#$8aW`(!y^T8(m4$q_9aQS8jHn+|NGm_&n zZ_oqZ$9Tz*Kl=9d5n`2%#3x{}i;q{yrf;15iE9UW@PlcFS~H7a_1fuli@T(?bw_yZ_?6PjYx_Ab1OmOG^UV%3Hw zeq5|-+*3s7wO@{)Ge=X5HDHdC3#YHv1f16P0hZK0Q9Bw{94y_q3#6apjQqI}RM6o# zaJ1*Vn)8+Up72MsiL-uh04A{kL`En{hGLH60T8~)6%}VyuPSp(~eO*s?D21!m;VOd9QeFO>JC zESPua>MzcWnsuM!lb=ys^GGrF{GiMuF-`9w_;P7PxN#=4bE=2l-Tmnu>=st%o))V} z(n1?4|2(OyIcj~A>j2#A4UazY+5_3Otjs%=i!|?0Jy`WO#IRkMDK3bU7&ZfAC#`aL zCYOmBe8k{l3;w`nHti>aZk)z5pQM>SO(!seUCq=hvZh(iY{UackTL^IAW~~=NxPF# z)%{g&Ju3CdD&5nzUb4!(2Fjfq=3;ja0jkrS3>;lZ|E6z5qTZ3->K6lAGI`p2Z`5s# zH*tmY!0Yiqi13C{~cLw(9lOsm~%#5)Ev<;5>0by zbgesgTJlk(JkXf>Y}al?VG_Xl;#`5FDQ0lWne@V{_foD6B!%#lv zyE?>eQpUH8DBR)I()?4=zbYVmM^Tjvt8Z_6eR_sFVbpCXt?p2~KcfZ z#hYt~`PFFafaiI=khopv4fGrsgS4xntUd-QG5{5GD+9Y*q(NI2XTEc%BQ^f*~#M7NbwJ`?pXkpzfV60CWyE3G4@+aXDm%c zN?f0N(pwCajYgfEmXw^O6)9wK8?RLQ`LQ?zXQ*4eV!)nxJa*cGrAq2!=w=+mK5q9d zjNCmQh@nIprahQG-@G@d9J5IfZvJj9{l06Mq!kdC)Syo7_b>hXW7eoNT>yvzh%z@Y z1dI6*!WC(NTq=3~lbHH=+{zU-!U{i^ElMgyKjE#4@jsBg4ko;+i%;FIj2^rKgtnV~TTh1RhypNsjhhNbLaFH5h^APKB$ zl$ZRv6}Bc?3OlDe@C2A>+13qW7Tj86a2Pu(NPrP~);=b~a6Dc#u@SU0pB+zj_wakd z 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 O{`4*w=@gu|d+ @@ -117,3 +118,13 @@ This repository contains notebook examples for the Bedrock Architecture Patterns ### 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 + +### Code Generation, SQL Generation, Code Translation and Explanation + +1. [Code Generation](./06_CodeGeneration/00_code_generatation_w_bedrock.ipynb)- Demonstrates how to generate Python code using Natural language. It shows examples of prompting to generate simple functions, classes, and full programs in Python for Data Analyst to perform sales analysis on a given Sales CSV dataset. + +2. [Database or SQL Query Generation](./06_CodeGeneration/01_sql_query_generate_w_bedrock.ipynb) - Focuses on generating SQL queries with Amazon Bedrock APIs. It includes examples of generating both simple and complex SQL statements for a given data set and database schema. + +3. [Code Explanation](./06_CodeGeneration/02_code_interpret_w_langchain.ipynb) - Uses Bedrock's foundation models to generate explanations for complex C++ code snippets. It shows how to carefully craft prompts to get the model to generate comments and documentation that explain the functionality and logic of complicated C++ code examples. Prompts can be easily updated for another programming languages. + +4. [Code Translation ](./06_CodeGeneration/03_code_translate_w_langchain.ipynb) - Guides you through translating C++ code to Java using Amazon Bedrock and LangChain APIs. It shows techniques for prompting the model to port C++ code over to Java, handling differences in syntax, language constructs, and conventions between the languages.