|
| 1 | +# Topic |
| 2 | + |
| 3 | +> This tutorial explains detail steps if you want to configure connector to use Bedrock model in another AWS account. This cross-account model invocation feature starts from 2.15. |
| 4 | + |
| 5 | +> Bedrock has [quota limit](https://docs.aws.amazon.com/bedrock/latest/userguide/quotas.html). You can purchase [Provisioned Throughput](https://docs.aws.amazon.com/bedrock/latest/userguide/prov-throughput.html) to increase quota limit. |
| 6 | +
|
| 7 | +This doc introduces how to build semantic search in Amazon managed OpenSearch with [Bedrock Titan embedding model](https://docs.aws.amazon.com/bedrock/latest/userguide/titan-embedding-models.html) in another AWS account. |
| 8 | +If you are not using Amazon OpenSearch, you can refer to [bedrock_connector_titan_embedding_blueprint](https://github.com/opensearch-project/ml-commons/blob/2.x/docs/remote_inference_blueprints/bedrock_connector_titan_embedding_blueprint.md). |
| 9 | + |
| 10 | +Note: You should replace the placeholders with prefix `your_` with your own value |
| 11 | + |
| 12 | +In this tutorial, we will use two AWS accounts. One for running OpenSearch cluster, we call it account A; the other for invoking Bedrock model, we call it account B. |
| 13 | + |
| 14 | +# Steps |
| 15 | + |
| 16 | +The support cross account model invokation, we need to configure two roles in connector credential part: |
| 17 | +- `roleArn`: role in account A which will be used to assume external account role in account B |
| 18 | +- `externalAccountRoleArn`: role in account B which will be used to invoke Bedrock model. |
| 19 | + |
| 20 | +In this tutorial , we will use such role name |
| 21 | +- Account A: `my_cross_account_role`, `roleArn` will be `arn:aws:iam::<your_aws_account_A>:role/my_cross_account_role_accountA` |
| 22 | +- Account B: `my_invoke_bedrock_role`, `externalAccountRoleArn` will be `arn:aws:iam::<your_aws_account_B>:role/my_invoke_bedrock_role_accountB` |
| 23 | + |
| 24 | +## 0. Create OpenSearch cluster in Account A |
| 25 | + |
| 26 | +Go to AWS OpenSearch console UI and create OpenSearch domain. |
| 27 | + |
| 28 | +Note the domain ARN which will be used in later steps. |
| 29 | + |
| 30 | + |
| 31 | +## 1. Create IAM role in Account B |
| 32 | +To invoke Bedrock model, we need to create an IAM role with proper permission. |
| 33 | +This IAM role will be configured in connector. Connector will use this role to invoke Bedrock model. |
| 34 | + |
| 35 | +Go to IAM console, create IAM role `my_invoke_bedrock_role_accountB` with: |
| 36 | + |
| 37 | +- Custom trust policy: |
| 38 | +``` |
| 39 | +{ |
| 40 | + "Version": "2012-10-17", |
| 41 | + "Statement": [ |
| 42 | + { |
| 43 | + "Sid": "", |
| 44 | + "Effect": "Allow", |
| 45 | + "Principal": { |
| 46 | + "AWS": "arn:aws:iam::<your_aws_account_A>:role/my_cross_account_role_accountA" |
| 47 | + }, |
| 48 | + "Action": "sts:AssumeRole" |
| 49 | + } |
| 50 | + ] |
| 51 | +} |
| 52 | +``` |
| 53 | +- Permission |
| 54 | +``` |
| 55 | +{ |
| 56 | + "Version": "2012-10-17", |
| 57 | + "Statement": [ |
| 58 | + { |
| 59 | + "Action": [ |
| 60 | + "bedrock:InvokeModel" |
| 61 | + ], |
| 62 | + "Effect": "Allow", |
| 63 | + "Resource": "arn:aws:bedrock:*::foundation-model/amazon.titan-embed-text-v1" |
| 64 | + } |
| 65 | + ] |
| 66 | +} |
| 67 | +``` |
| 68 | + |
| 69 | +## 2. Create IAM role in account A |
| 70 | + |
| 71 | +### 2.1 Create IAM role for assuming externalAccountRoleArn |
| 72 | +Create an IAM role which can assume `externalAccountRoleArn` in account B. |
| 73 | + |
| 74 | +Go to IAM console, create IAM role `my_cross_account_role_accountA` with: |
| 75 | + |
| 76 | +- Custom trust policy: |
| 77 | +``` |
| 78 | +{ |
| 79 | + "Version": "2012-10-17", |
| 80 | + "Statement": [ |
| 81 | + { |
| 82 | + "Effect": "Allow", |
| 83 | + "Principal": { |
| 84 | + "Service": "es.amazonaws.com" |
| 85 | + }, |
| 86 | + "Action": "sts:AssumeRole" |
| 87 | + } |
| 88 | + ] |
| 89 | +} |
| 90 | +``` |
| 91 | +- Permission |
| 92 | +``` |
| 93 | +{ |
| 94 | + "Version": "2012-10-17", |
| 95 | + "Statement": [ |
| 96 | + { |
| 97 | + "Effect": "Allow", |
| 98 | + "Action": "sts:AssumeRole", |
| 99 | + "Resource": "arn:aws:iam::<your_aws_account_B>:role/my_invoke_bedrock_role_accountB" |
| 100 | + } |
| 101 | + ] |
| 102 | +} |
| 103 | +``` |
| 104 | + |
| 105 | +### 2.2 Create IAM role for Signing create connector request |
| 106 | + |
| 107 | +Generate a new IAM role specifically for signing your create connector request. |
| 108 | + |
| 109 | + |
| 110 | +Create IAM role `my_create_connector_role_accountA` with |
| 111 | +- Custom trust policy. Note: `your_iam_user_arn` is the IAM user which will run `aws sts assume-role` in step 3.1 |
| 112 | +``` |
| 113 | +{ |
| 114 | + "Version": "2012-10-17", |
| 115 | + "Statement": [ |
| 116 | + { |
| 117 | + "Effect": "Allow", |
| 118 | + "Principal": { |
| 119 | + "AWS": "your_iam_user_arn" |
| 120 | + }, |
| 121 | + "Action": "sts:AssumeRole" |
| 122 | + } |
| 123 | + ] |
| 124 | +} |
| 125 | +``` |
| 126 | +- permission |
| 127 | +``` |
| 128 | +{ |
| 129 | + "Version": "2012-10-17", |
| 130 | + "Statement": [ |
| 131 | + { |
| 132 | + "Effect": "Allow", |
| 133 | + "Action": "iam:PassRole", |
| 134 | + "Resource": "arn:aws:iam::<your_aws_account_A>:role/my_cross_account_role_accountA" |
| 135 | + }, |
| 136 | + { |
| 137 | + "Effect": "Allow", |
| 138 | + "Action": "es:ESHttpPost", |
| 139 | + "Resource": "your_opensearch_domain_arn_created_in_step0" |
| 140 | + } |
| 141 | + ] |
| 142 | +} |
| 143 | +``` |
| 144 | + |
| 145 | +Copy this role ARN which will be used in later steps. |
| 146 | + |
| 147 | +### 2.3 Map backend role |
| 148 | + |
| 149 | +1. Log in to your OpenSearch Dashboard and navigate to the "Security" page, which you can find in the left-hand menu. |
| 150 | +2. Then click "Roles" on security page (you can find it on left-hand), then find "ml_full_access" role and click it. |
| 151 | +3. On "ml_full_access" role detail page, click "Mapped users", then click "Manage mapping". Paste IAM role ARN created in step 2.2 to backend roles part: `arn:aws:iam::<your_aws_account_A>:role/my_create_connector_role_accountA`. |
| 152 | +Click "Map", then the IAM role configured successfully in your OpenSearch cluster. |
| 153 | + |
| 154 | + |
| 155 | + |
| 156 | +## 3. Create Connector |
| 157 | + |
| 158 | +Find more details on [connector](https://opensearch.org/docs/latest/ml-commons-plugin/remote-models/connectors/) |
| 159 | + |
| 160 | + |
| 161 | +### 3.1 Get temporary credential of the role created in step 2.2: |
| 162 | +Use your IAM user credential to assume role created in step 2.2 |
| 163 | +``` |
| 164 | +aws sts assume-role --role-arn arn:aws:iam::<your_aws_account_A>:role/my_create_connector_role_accountA --role-session-name your_session_name |
| 165 | +``` |
| 166 | + |
| 167 | +Configure the temporary credential in `~/.aws/credentials` like this |
| 168 | + |
| 169 | +``` |
| 170 | +[default] |
| 171 | +AWS_ACCESS_KEY_ID=your_access_key_of_role_created_in_step2.2 |
| 172 | +AWS_SECRET_ACCESS_KEY=your_secret_key_of_role_created_in_step2.2 |
| 173 | +AWS_SESSION_TOKEN=your_session_token_of_role_created_in_step2.2 |
| 174 | +``` |
| 175 | + |
| 176 | +### 3.2 Create connector |
| 177 | + |
| 178 | +Run this python code with the temporary credential configured in `~/.aws/credentials` |
| 179 | + |
| 180 | +``` |
| 181 | +import boto3 |
| 182 | +import requests |
| 183 | +from requests_aws4auth import AWS4Auth |
| 184 | +
|
| 185 | +host = 'your_amazon_opensearch_domain_endpoint_created_in_step0' |
| 186 | +region = 'your_amazon_opensearch_domain_region' |
| 187 | +service = 'es' |
| 188 | +
|
| 189 | +credentials = boto3.Session().get_credentials() |
| 190 | +awsauth = AWS4Auth(credentials.access_key, credentials.secret_key, region, service, session_token=credentials.token) |
| 191 | +
|
| 192 | +path = '/_plugins/_ml/connectors/_create' |
| 193 | +url = host + path |
| 194 | +
|
| 195 | +bedrock_model_region='your_bedrock_model_region' |
| 196 | +payload = { |
| 197 | + "name": "Amazon Bedrock Connector: titan embedding v1", |
| 198 | + "description": "The connector to bedrock Titan embedding model", |
| 199 | + "version": 1, |
| 200 | + "protocol": "aws_sigv4", |
| 201 | + "parameters": { |
| 202 | + "region": bedrock_model_region, |
| 203 | + "service_name": "bedrock" |
| 204 | + }, |
| 205 | + "credential": { |
| 206 | + "roleArn": "arn:aws:iam::<your_aws_account_A>:role/my_cross_account_role_accountA" |
| 207 | + "externalAccountRoleArn": "arn:aws:iam::<your_aws_account_B>:role/my_invoke_bedrock_role_accountB" |
| 208 | + }, |
| 209 | + "actions": [ |
| 210 | + { |
| 211 | + "action_type": "predict", |
| 212 | + "method": "POST", |
| 213 | + "url": f"https://bedrock-runtime.{bedrock_model_region}.amazonaws.com/model/amazon.titan-embed-text-v1/invoke", |
| 214 | + "headers": { |
| 215 | + "content-type": "application/json", |
| 216 | + "x-amz-content-sha256": "required" |
| 217 | + }, |
| 218 | + "request_body": "{ \"inputText\": \"${parameters.inputText}\" }", |
| 219 | + "pre_process_function": "connector.pre_process.bedrock.embedding", |
| 220 | + "post_process_function": "connector.post_process.bedrock.embedding" |
| 221 | + } |
| 222 | + ] |
| 223 | +} |
| 224 | +
|
| 225 | +headers = {"Content-Type": "application/json"} |
| 226 | +
|
| 227 | +r = requests.post(url, auth=awsauth, json=payload, headers=headers) |
| 228 | +print(r.text) |
| 229 | +``` |
| 230 | +The script will output connector id. |
| 231 | + |
| 232 | +sample output |
| 233 | +``` |
| 234 | +{"connector_id":"N0qpQY0BOhavBOmfOCnw"} |
| 235 | +``` |
| 236 | + |
| 237 | +Copy connector id which will be used in later steps. |
| 238 | + |
| 239 | +## 4. Create Model and test |
| 240 | + |
| 241 | +Login your OpenSearch Dashboard, open DevTools, then run these |
| 242 | + |
| 243 | +1. Create model group |
| 244 | +``` |
| 245 | +POST /_plugins/_ml/model_groups/_register |
| 246 | +{ |
| 247 | + "name": "Bedrock_embedding_model", |
| 248 | + "description": "Test model group for bedrock embedding model" |
| 249 | +} |
| 250 | +``` |
| 251 | +Sample output |
| 252 | +``` |
| 253 | +{ |
| 254 | + "model_group_id": "LxWiQY0BTaDH9c7t9xeE", |
| 255 | + "status": "CREATED" |
| 256 | +} |
| 257 | +``` |
| 258 | + |
| 259 | +2. Register model |
| 260 | + |
| 261 | +``` |
| 262 | +POST /_plugins/_ml/models/_register |
| 263 | +{ |
| 264 | + "name": "bedrock titan embedding model v1", |
| 265 | + "function_name": "remote", |
| 266 | + "description": "test embedding model", |
| 267 | + "model_group_id": "LxWiQY0BTaDH9c7t9xeE", |
| 268 | + "connector_id": "N0qpQY0BOhavBOmfOCnw" |
| 269 | +} |
| 270 | +``` |
| 271 | +Sample output |
| 272 | +``` |
| 273 | +{ |
| 274 | + "task_id": "O0q3QY0BOhavBOmf1SmL", |
| 275 | + "status": "CREATED", |
| 276 | + "model_id": "PEq3QY0BOhavBOmf1Sml" |
| 277 | +} |
| 278 | +``` |
| 279 | + |
| 280 | +3. Deploy model |
| 281 | +``` |
| 282 | +POST /_plugins/_ml/models/PEq3QY0BOhavBOmf1Sml/_deploy |
| 283 | +``` |
| 284 | +Sample output |
| 285 | +``` |
| 286 | +{ |
| 287 | + "task_id": "PUq4QY0BOhavBOmfBCkQ", |
| 288 | + "task_type": "DEPLOY_MODEL", |
| 289 | + "status": "COMPLETED" |
| 290 | +} |
| 291 | +``` |
| 292 | +4. Predict |
| 293 | +``` |
| 294 | +POST /_plugins/_ml/models/PEq3QY0BOhavBOmf1Sml/_predict |
| 295 | +{ |
| 296 | + "parameters": { |
| 297 | + "inputText": "hello world" |
| 298 | + } |
| 299 | +} |
| 300 | +``` |
| 301 | +Sample response |
| 302 | +``` |
| 303 | +{ |
| 304 | + "inference_results": [ |
| 305 | + { |
| 306 | + "output": [ |
| 307 | + { |
| 308 | + "name": "sentence_embedding", |
| 309 | + "data_type": "FLOAT32", |
| 310 | + "shape": [ |
| 311 | + 1536 |
| 312 | + ], |
| 313 | + "data": [ |
| 314 | + 0.7265625, |
| 315 | + -0.0703125, |
| 316 | + 0.34765625, |
| 317 | + ...] |
| 318 | + } |
| 319 | + ], |
| 320 | + "status_code": 200 |
| 321 | + } |
| 322 | + ] |
| 323 | +} |
| 324 | +``` |
| 325 | + |
| 326 | +## 5. Semantic search |
| 327 | + |
| 328 | +### 5.1 create ingest pipeline |
| 329 | +Find more details: [ingest pipeline](https://opensearch.org/docs/latest/ingest-pipelines/) |
| 330 | + |
| 331 | +``` |
| 332 | +PUT /_ingest/pipeline/my_bedrock_embedding_pipeline |
| 333 | +{ |
| 334 | + "description": "text embedding pentest", |
| 335 | + "processors": [ |
| 336 | + { |
| 337 | + "text_embedding": { |
| 338 | + "model_id": "your_bedrock_embedding_model_id_created_in_step4", |
| 339 | + "field_map": { |
| 340 | + "text": "text_knn" |
| 341 | + } |
| 342 | + } |
| 343 | + } |
| 344 | + ] |
| 345 | +} |
| 346 | +``` |
| 347 | +### 5.2 create k-NN index |
| 348 | +Find more details: [k-NN index](https://opensearch.org/docs/latest/search-plugins/knn/knn-index/) |
| 349 | + |
| 350 | +You should customize your k-NN index for better performance. |
| 351 | +``` |
| 352 | +PUT my_index |
| 353 | +{ |
| 354 | + "settings": { |
| 355 | + "index": { |
| 356 | + "knn.space_type": "cosinesimil", |
| 357 | + "default_pipeline": "my_bedrock_embedding_pipeline", |
| 358 | + "knn": "true" |
| 359 | + } |
| 360 | + }, |
| 361 | + "mappings": { |
| 362 | + "properties": { |
| 363 | + "text_knn": { |
| 364 | + "type": "knn_vector", |
| 365 | + "dimension": 1536 |
| 366 | + } |
| 367 | + } |
| 368 | + } |
| 369 | +} |
| 370 | +``` |
| 371 | +### 5.3 ingest test data |
| 372 | +``` |
| 373 | +POST /my_index/_doc/1000001 |
| 374 | +{ |
| 375 | + "text": "hello world." |
| 376 | +} |
| 377 | +``` |
| 378 | +### 5.4 search |
| 379 | +Find more details: [neural search](https://opensearch.org/docs/latest/search-plugins/neural-search/). |
| 380 | +``` |
| 381 | +POST /my_index/_search |
| 382 | +{ |
| 383 | + "query": { |
| 384 | + "neural": { |
| 385 | + "text_knn": { |
| 386 | + "query_text": "hello", |
| 387 | + "model_id": "your_embedding_model_id_created_in_step4", |
| 388 | + "k": 100 |
| 389 | + } |
| 390 | + } |
| 391 | + }, |
| 392 | + "size": "1", |
| 393 | + "_source": ["text"] |
| 394 | +} |
| 395 | +``` |
0 commit comments