From c2fe5cd9a7d85782ff318abda4998bf691785b0e Mon Sep 17 00:00:00 2001 From: Mike Hume Date: Wed, 8 Jul 2026 11:41:32 +0100 Subject: [PATCH] New serverless pattern - strands-agentcore-smithy --- strands-agentcore-smithy/.gitignore | 15 + strands-agentcore-smithy/Makefile | 31 + strands-agentcore-smithy/README.md | 184 + .../architecture/target-smithy.png | Bin 0 -> 69771 bytes strands-agentcore-smithy/example-pattern.json | 86 + .../bedrock-runtime-2023-09-30.json | 7561 +++++++++++++++++ .../infrastructure/template.yaml | 219 + strands-agentcore-smithy/requirements.txt | 4 + strands-agentcore-smithy/scripts/deploy.sh | 240 + strands-agentcore-smithy/scripts/test.sh | 78 + .../src/agent/__init__.py | 4 + .../src/agent/agent_processor.py | 77 + strands-agentcore-smithy/src/agent/handler.py | 108 + .../src/agent/strands_client.py | 71 + .../src/shared/__init__.py | 36 + .../src/shared/error_utils.py | 75 + .../src/shared/jwt_utils.py | 96 + .../src/shared/logging_utils.py | 74 + strands-agentcore-smithy/src/shared/models.py | 29 + strands-agentcore-smithy/tests/__init__.py | 1 + strands-agentcore-smithy/tests/conftest.py | 140 + .../tests/property/__init__.py | 1 + .../property/test_agnostic_properties.py | 82 + .../test_cloudformation_properties.py | 252 + .../property/test_deploy_script_properties.py | 50 + .../tests/property/test_jwt_properties.py | 120 + .../tests/unit/__init__.py | 1 + .../tests/unit/test_agent_processor.py | 143 + .../unit/test_cloudformation_template.py | 363 + .../tests/unit/test_deploy_script.py | 192 + .../tests/unit/test_error_utils.py | 60 + .../tests/unit/test_handler.py | 131 + .../tests/unit/test_jwt_utils.py | 98 + .../tests/unit/test_logging_utils.py | 56 + .../tests/unit/test_models.py | 54 + .../tests/unit/test_strands_client.py | 115 + .../tests/unit/test_test_script.py | 142 + 37 files changed, 10989 insertions(+) create mode 100644 strands-agentcore-smithy/.gitignore create mode 100644 strands-agentcore-smithy/Makefile create mode 100644 strands-agentcore-smithy/README.md create mode 100644 strands-agentcore-smithy/architecture/target-smithy.png create mode 100644 strands-agentcore-smithy/example-pattern.json create mode 100644 strands-agentcore-smithy/infrastructure/bedrock-runtime-2023-09-30.json create mode 100644 strands-agentcore-smithy/infrastructure/template.yaml create mode 100644 strands-agentcore-smithy/requirements.txt create mode 100755 strands-agentcore-smithy/scripts/deploy.sh create mode 100755 strands-agentcore-smithy/scripts/test.sh create mode 100644 strands-agentcore-smithy/src/agent/__init__.py create mode 100644 strands-agentcore-smithy/src/agent/agent_processor.py create mode 100644 strands-agentcore-smithy/src/agent/handler.py create mode 100644 strands-agentcore-smithy/src/agent/strands_client.py create mode 100644 strands-agentcore-smithy/src/shared/__init__.py create mode 100644 strands-agentcore-smithy/src/shared/error_utils.py create mode 100644 strands-agentcore-smithy/src/shared/jwt_utils.py create mode 100644 strands-agentcore-smithy/src/shared/logging_utils.py create mode 100644 strands-agentcore-smithy/src/shared/models.py create mode 100644 strands-agentcore-smithy/tests/__init__.py create mode 100644 strands-agentcore-smithy/tests/conftest.py create mode 100644 strands-agentcore-smithy/tests/property/__init__.py create mode 100644 strands-agentcore-smithy/tests/property/test_agnostic_properties.py create mode 100644 strands-agentcore-smithy/tests/property/test_cloudformation_properties.py create mode 100644 strands-agentcore-smithy/tests/property/test_deploy_script_properties.py create mode 100644 strands-agentcore-smithy/tests/property/test_jwt_properties.py create mode 100644 strands-agentcore-smithy/tests/unit/__init__.py create mode 100644 strands-agentcore-smithy/tests/unit/test_agent_processor.py create mode 100644 strands-agentcore-smithy/tests/unit/test_cloudformation_template.py create mode 100644 strands-agentcore-smithy/tests/unit/test_deploy_script.py create mode 100644 strands-agentcore-smithy/tests/unit/test_error_utils.py create mode 100644 strands-agentcore-smithy/tests/unit/test_handler.py create mode 100644 strands-agentcore-smithy/tests/unit/test_jwt_utils.py create mode 100644 strands-agentcore-smithy/tests/unit/test_logging_utils.py create mode 100644 strands-agentcore-smithy/tests/unit/test_models.py create mode 100644 strands-agentcore-smithy/tests/unit/test_strands_client.py create mode 100644 strands-agentcore-smithy/tests/unit/test_test_script.py diff --git a/strands-agentcore-smithy/.gitignore b/strands-agentcore-smithy/.gitignore new file mode 100644 index 0000000000..6475b62f13 --- /dev/null +++ b/strands-agentcore-smithy/.gitignore @@ -0,0 +1,15 @@ +*-deps/ +*-package/ +*.zip +.aws-sam/ +samconfig.toml +.kiro/ +.venv/ +venv/ +__pycache__/ +*.pyc +*.so +*.egg-info/ +.pytest_cache/ +.hypothesis/ +.DS_Store diff --git a/strands-agentcore-smithy/Makefile b/strands-agentcore-smithy/Makefile new file mode 100644 index 0000000000..0a8a6589d6 --- /dev/null +++ b/strands-agentcore-smithy/Makefile @@ -0,0 +1,31 @@ +# ============================================================ +# AgentCore Smithy Bedrock - SAM Makefile build target +# ============================================================ +# Used by `sam build` via the AgentLambdaFunction resource's +# `Metadata: BuildMethod: makefile`. This replaces the previous +# manual packaging in deploy.sh and requires NO Docker. +# +# SAM invokes `make build-AgentLambdaFunction` with ARTIFACTS_DIR +# set to the directory SAM expects the built artifacts in. +# +# Two-step install reproduces the original packaging behaviour: +# 1. Install all dependencies as manylinux2014 x86_64 wheels +# 2. Re-install pure-Python packages with --no-deps so nothing +# is skipped by the binary-only constraint +# ============================================================ + +build-AgentLambdaFunction: + pip3 install \ + --target "$(ARTIFACTS_DIR)" \ + --platform manylinux2014_x86_64 \ + --python-version 3.12 \ + --only-binary=:all: \ + -r requirements.txt + pip3 install \ + --target "$(ARTIFACTS_DIR)" \ + --platform manylinux2014_x86_64 \ + --python-version 3.12 \ + --only-binary=:all: \ + --no-deps \ + requests urllib3 charset-normalizer idna certifi PyJWT cryptography cffi + cp -r src "$(ARTIFACTS_DIR)/" diff --git a/strands-agentcore-smithy/README.md b/strands-agentcore-smithy/README.md new file mode 100644 index 0000000000..d6e8b71889 --- /dev/null +++ b/strands-agentcore-smithy/README.md @@ -0,0 +1,184 @@ +# Strands Agent on Lambda with AgentCore Smithy Bedrock Target + +A serverless AI agent that uses AWS Bedrock AgentCore Gateway with a Smithy model target to call Bedrock Runtime. The agent (Claude Sonnet 4.6) calls another model (Claude Haiku 4.5) as a tool via the gateway's Converse API. + +## Architecture + +![Architecture Diagram](architecture/target-smithy.png) + +``` +User → test.sh → Agent Lambda ──────────────→ AgentCore Gateway (MCP) ──────────→ Bedrock Runtime + │ │ │ + Strands Agent CUSTOM_JWT Auth GATEWAY_IAM_ROLE + + BedrockModel (Cognito JWKS) (IAM role signs + (Sonnet 4.6) + MCP Protocol requests — no + + MCPClient + Tool Discovery API key needed) + │ + SmithyModel target │ + Cognito JWT (loaded from S3) Converse API call + validated → Claude Haiku 4.5 +``` + +## How It Works + +1. User authenticates with Cognito and invokes the Lambda with a natural language prompt +2. The Lambda runs a Strands SDK agent with Claude Sonnet 4.6 as the LLM +3. The agent connects to AgentCore Gateway via MCP and discovers available tools +4. The gateway reads the official Bedrock Runtime Smithy model (from S3) and exposes operations as MCP tools +5. Sonnet decides to call the Converse tool, passing the user's prompt to Claude Haiku 4.5 +6. The gateway assumes its IAM execution role, signs the request, and calls Bedrock Runtime +7. Haiku's response flows back through the gateway → MCP → agent → user + +## Deploy + +### Step 1: Prerequisites + +- AWS CLI v2 +- AWS SAM CLI +- Python 3.12+ +- pip3 +- `make` (used by the SAM Makefile build — no Docker required) +- AWS account with Bedrock and AgentCore enabled in `us-east-1` +- Bedrock model access enabled for Claude Sonnet 4.6 and Claude Haiku 4.5 + +### Step 2: Open a Terminal + +Open a terminal on your machine and navigate to where you want to clone the project. + +### Step 3: Clone the Repository + +```bash +git clone https://github.com/aws-samples/serverless-patterns +cd serverless-patterns/strands-agentcore-smithy +``` + +### Step 4: Deploy + +One command deploys everything: + +```bash +bash scripts/deploy.sh +``` + +This will: +1. Validate the SAM template (`sam validate`) +2. Download the official Bedrock Runtime Smithy model and upload to S3 +3. Build the Lambda with `sam build` (Makefile build — two-step pip3 install, no Docker) +4. Deploy the stack with `sam deploy` (Gateway, Cognito, Lambda, IAM roles) +5. Create a Cognito test user +6. Generate `scripts/test.sh` with baked-in config values + +## Test + +After deployment, test with: + +```bash +./scripts/test.sh "Ask Haiku to write a short poem about the Beatles" +``` + +Or use the default prompt: + +```bash +./scripts/test.sh +``` + +## Project Structure + +``` +├── infrastructure/ +│ ├── template.yaml # SAM template (all AWS resources) +│ └── bedrock-runtime-2023-09-30.json # Official Smithy model (for tests) +├── Makefile # SAM Makefile build (no Docker) +├── scripts/ +│ ├── deploy.sh # Full deployment script (SAM) +│ └── test.sh # Generated by deploy.sh +├── src/ +│ ├── agent/ +│ │ ├── handler.py # Lambda entry point +│ │ ├── agent_processor.py # Strands Agent + MCP lifecycle +│ │ └── strands_client.py # MCP client + Bedrock model factories +│ └── shared/ +│ ├── models.py # UserContext, AgentRequest, AgentResponse +│ ├── jwt_utils.py # JWT validation (Cognito ID tokens) +│ ├── error_utils.py # Error response formatting +│ └── logging_utils.py # Structured logging +├── tests/ +│ ├── unit/ # Unit tests +│ └── property/ # Property-based tests (Hypothesis) +├── requirements.txt # Lambda runtime dependencies +└── README.md +``` + +## Changing the Outer Agent Model + +The outer agent model is set in `src/agent/strands_client.py`: + +```python +# Default Bedrock model ID for Claude Sonnet 4.6 +DEFAULT_MODEL_ID = "us.anthropic.claude-sonnet-4-6" +``` + +To use a different model, update `DEFAULT_MODEL_ID` to any Bedrock cross-region inference profile ID you have access to. For example: + +```python +DEFAULT_MODEL_ID = "us.anthropic.claude-3-5-sonnet-20241022-v2:0" +``` + +Make sure the model is enabled in your AWS account under **Bedrock → Model access** before deploying. + +> **Note:** The inner model (the one the agent calls as a tool) is separate — it's configured in the `SYSTEM_PROMPT` string in `src/agent/agent_processor.py`. See [Changing the Inner Model](#changing-the-inner-model) and [Key Design Decisions](#key-design-decisions) for more detail. + +## Changing the Inner Model + +The inner model is the one the outer agent calls *as a tool* via the AgentCore Gateway. It's configured inside the `SYSTEM_PROMPT` string in `src/agent/agent_processor.py`: + +```python +SYSTEM_PROMPT = """You have access to Bedrock Runtime tools via MCP. Use the Converse tool (not InvokeModel) to call another model. + +When using bedrock-runtime-target___Converse: +- Set modelId to: anthropic.claude-haiku-4-5-20251001-v1:0 +... +``` + +To use a different model, replace the `modelId` value in that instruction: + +```python +- Set modelId to: anthropic.claude-haiku-4-5-20251001-v1:0 +``` + +A few things to keep in mind: + +- The model ID here is passed as a parameter in the MCP tool call, not used directly by the Lambda — so it must be a valid Bedrock `modelId` (not a cross-region inference profile). +- Make sure the model supports the **Converse API** (most Claude models do, but check the [Bedrock docs](https://docs.aws.amazon.com/bedrock/latest/userguide/conversation-inference-supported-models-features.html)). +- Enable the model in your AWS account under **Bedrock → Model access** before deploying. + +## Key Design Decisions + +### Official Smithy Model (not custom) +Custom Smithy models don't work for AWS service targets — the gateway needs the full endpoint resolution metadata (`aws.api#service` trait, partition data, FIPS/DualStack logic) that only official models have. The deploy script downloads the official model from the [AWS API Models repo](https://github.com/aws/api-models-aws). + +### Converse API (not InvokeModel) +The `InvokeModel` operation uses a `Blob` with `httpPayload` trait for the request body, which doesn't map to MCP tool input parameters. `Converse` has structured `messages` and `modelId` fields that map cleanly. + +### GATEWAY_IAM_ROLE (not API_KEY) +Since the target is an AWS service (Bedrock Runtime), the gateway uses its IAM execution role to sign requests. No API keys, no Secrets Manager secrets, no credential provider CLI commands. + +### Agent code is target-agnostic +All code in `src/agent/` and `src/shared/` connects to the gateway via MCP and doesn't reference Bedrock, DynamoDB, or any specific target. The same code works with any gateway target type. + +## Teardown + +```bash +aws cloudformation delete-stack --stack-name agentcore-smithy-bedrock --region us-east-1 +aws s3 rb s3://agentcore-smithy-bedrock-smithy-models --force --region us-east-1 +``` + +## Run Tests + +```bash +python -m pytest tests/ -v +``` + +--- + +Copyright 2026 Amazon.com, Inc. or its affiliates. All Rights Reserved. +SPDX-License-Identifier: MIT-0 diff --git a/strands-agentcore-smithy/architecture/target-smithy.png b/strands-agentcore-smithy/architecture/target-smithy.png new file mode 100644 index 0000000000000000000000000000000000000000..227e1291575a6edd2be370912c42c69943103d02 GIT binary patch literal 69771 zcmeFZcT|&I(>@9ah#*C23R09NUAlBE^xg?wflvcV?+B>$UPJGw^j-o)RC)jjHI$%8 zZ;>wj+`PZ{5uUTY^Z!}re5@66ubn%)%$~XCn%Q$FOhZkcn2?4L3k!=_QQ^537S=Uu zEG(Qg0(_vwTkYH#3kzGu_SrKH#b?hLHJqKmwhmTUSa*{AVq2B2J)w99PksC6I>$p{ zDx(h%pT|Alupp%5O8)R?7$V#wr!ZFux;6W`bF6oAlINw>PuhosQb7cSUxaDK#KY1) zEPwv#pCRh98K;?0TyI?eq46z2pZzrj z%l4N(ol2{S*FSLu^1NDSvwiHnI_{_XynWTup8)6SH2>4I5XJChmOO*>VP9Qa{oQS* z(Wm@F^I0^{V}gHJU3;0J0VfI?HzXTTFFcntKEl}NgS=EvNjw3OI#?Mff>l+qIDl&c zEL?0FEIi-}8+b`!)Be4d!+wZ$?LX}}SXd#pSh)YaM-6zt_)`F07vK3m?>IR)|J-rS zI_KIy*Enkzx2CTWk^yfwUMYZFu&{{8FJ9PKX&HC0uw<|lpFh#@#9p7d?n@zi{CSIw z@+s!Q1BH|aqi8%>XsgINrC(Xa41A&P;Jpcj_`Yf7LAa7O8SWGBij_lYfd&`~X8hC{ z#q5(Z>x-C4hN7StR?oryO$Wm;*8BJ0VBruj{{1)dQN}xyJ~No>O z;?*CYdb`F&Mn)bf-3s>~gu*pgRWe!65oXM5E-r=2T_ni-og#kooy}YuGdCJO+esC6 z4xVI^4MXKAJ?;{1N~>FcrL1MZSoU0-@POBGoQlVUmCi#{gD@g|<7|8ru1IpPUVFO5 zp%fH27?8mzB6$+&yW1b5Wqezd;f#r7WlFnTa)|=vG`?|~o?s_hGdrMF0Jk5{@XI@y z6=m~*@N#g(U(XIu^>$lf_SvkgYskd=t3QE9>>9kh_VN;n@v68r?u$=k0`HP5+mA^-0^ZFUL$(% z@Qh;;}Bq)!M6pEfIFqnl^WF2nP+aOjN#Aj zjSxBSwZ=>>ia-*Xs53kZGTcU)y73yOdFiGa629)! zr@5NYd;O5o(lB5SKHlA(Z1bKGJKT#|L5mSxwJ%s)jZ;A8Lv8DUYU{I~_PkY0f{V=q zA!fe8v+oUWKRPfs@n2%vx8Y6`PLAoCw%|Q*xh7DP?={Toe7MrpSJPnfD=N>#4U-=q zssB^T{j1gXNWXq>Xs)rCkuv<{J^X)6OhGBoTf}+)8z*LheDuSMXQJMJvbJ~EFnvqX zryS~SNA<=5Xm%u{xcFw+$y}G&MghsQqy2bRG33tL$jUY<2CnepMb&!ss0^xlb`f3h zU@^X~f=wl{BH+v?+sGe;YG4IUzdY`)>V!q`xUbA|X%CDE( zwc8Zd`>J(zy=<^eGh1}~EM>M&O5BMy3I55?nTXe@NeO*4&j}T^&Jr9pe+3Klvnug# zwc6TV*f%JG8y)aMjPj_=&UVwIzf&nTsESYQh=PcV5r3v2?^E~G;pehqkQPno&=h=W zAc4U@`#}KWM4m<%k$ffzTf-RgvQlmB8eCeAe2%Fq!ONhcC=Z(ZC3}9~zSGS|U3ZM@ zGV?c5Ygz^S$aSHelkSPXtZ1|rDLaUEKi+9L&z?S6HaOfEysLRAJg!LRf9D{TxO!jm z^d(=)!r|}>9}Fw+^y{*M;uI3Sw4AxIuI!|A`n#O=<4Z?1Qw!?|uBJJ}uZ2$n^4@{N zFV35^OwWGQ@O%A&{MmdLVns`wHN6W+q{mAH%`^<|e=XvLfjNk3WVNg~i!0|fXi`u6 z!#$VkoG}wAknbzAvy7a|o6YOSqT0NQr7FGS$`m5#Ei6#IU6MnA0QJD>iCYh%)Uyb2 zL;EaoYxay;oMeUf*^wg;%3DQ*1<9tpk7|&L9j<69i#&F}c4W&aDdXt14q7Q~N0zFa zz9vnolwE(z_;s-HIr{K~w!+loXCxCz?9jPKm5C7cangYV7wJl9IaK9aHiDX{!PGT5 z!)^Bo8uKndy6@78de}88f@=6G+Bn%vU*}6|^UnGOr)olV?Z@msyT2(NUYYW^Q~S<$ zZ+f}W8cD1*ow<0;d3MueKILP8H$zs#%Is8hQ0b72y#^)=sUiQn)BG4S$UhzSNaaE>+p{VnHdDPlG(Z)AgurC4^Vj||q1z+(~*9If% ztq`qd^bm&;EL%;4L8koMBR2ND- zfLMV3f*&fj9Yd~tZI|4?tNY>*qZA3EH^KiG2E z*5I)cp2V`@F)_Po-!^K>g7H5qYZ&C3J{?hJu~j?Y7u^W@;OCg#4|~1cplz+O8*RO> z%i4O-(O?QapY~L@3jfxhV3VC(W(=zC;kiB`oO*CWyU1&A(5SZ2a_y_y*5tTXLvX7f z_`YUDWScmudVKmIReu%$vzDVQ&jpAR4Ai*wv$(}CB(gd0Ib7!V{+d-ZOE8Jp(5A=| z;>17hl89(M%h{!YQ4k1nu6}WCO&p|)67&AEshQFlC1GP$vAj%ex|95i+tp@V4zUok z*=n1*(w+%!pA{p^|HYV8(CY|ia#fLh%_m6y2N z&sa$hashq|Cyap@F-#%O+6cN7Zk1v_V#h<$!wY(P(2s@Ng#d%9_v3fPf|QPUhsCTxBaiQq>lZA z)J)*t#=MR@mX;rylUAHXP00;F-Ve-yGzok2S}Y>HHY8ystpmn~yWILp!<|trrRk9B zDhZEcA@2Z-=F~8Uat6uHsVoj+Ibj!UI;I-+_IN;6v-LK3KWtLOb?W~9*@K!b zsEBl|khi*_mu>3eUVwq{Vf`=`LKOxPm@wHzi_A1;nwY}MciXDz@DEaK8Cn>d;C>uF z8;wyZ<6K-wnbk6Ncs!LDG*l5xvmcI>Z9wx@M4x{Fiy8)Vod-wv%L!nIDlXF3*F=kb z-ux(med2KbIX6hd}17;=i+AkwraLvrP zZ_3YPUDY!ku*m9y(Qw$t#LP4(VaREZ7mv4x3#46lZR=0X+l<+QKR7gFpeYOG#irG$ zeo2?bw)zRdC)UY=w!=|VN?-A}tn=4L3v2Yb&<_okNa7d~ee+zkwzuxiaI%MG)jkl; zhPAzhjcw+soUJ)IHGLuq+wX}qNE<2q#k!zYG~_oNo%YKoofoaFLAA9iT=S;+qjk!* zJe7|v*XU=W!SdePP=^$6zr=XI1e$Rlu!tk93>nI(k|adC@TPqS$FEH|Lm% z-JN;qR@HO|oqeQ=lQkC6b>gWGeCsA}_7(0JDbDegukb!)a5cHhN>RF+n58r<)h659 zHQJctvB%GseiWYp2jM1=^4VYfMZ8a=VE|@Kk+)$aKkd~CwO?C$_u1>I-#hc&tu7EF zp_sI3R!6Y#mEho=%xJlyayXBy$oAb<*YCtZ(X>?_y923p&kiR0qYa?_ztHE;U7Pn) z@3ohyWoddV&h7^*vr)W``CW@Gw0apla5(=egqE`7bFm>k~-ncu3*9Mg7~qi7M7YW|?|3M=SEYg=+^@63%`{ zRIE|#;yEvA^LAewY<(~z=2m&oLPSu8JDSTiAQb!QPBFz@aY@KUud&igW1V5iSmC_j)DVBJ#eHGM{DhFk_u+eJuK z%MLoakNQ431AloATUUD!A7{FM4F(&uM07u-#S|5%!$O}U{YXVNOH!6wMUV7W7F1HQ zit}Hx#tJs1K=0lN@Lkb7uC&)4z_oYs+pg0Q{F>84osRSqxSjS()qm-(DELp7JH^n5 zEq$j0*ytXzq1f21Q$d_R>6XA$!Bm6isc_fMkzNOgRuwf&A}!Mvuysq+@tY}V zg(M*;qJVnXdN9c=B6)c|wsrT#_|OvtReD%U^Um)5vGY}CI+xuh6TEltkgeMCD+L1L zo%n(aNfi#+i1pZrJ*|Q8RrFc6!2HxKYtMJc^}a53v=RFLHI(Do38G1)M}mnLoll1F zd!JBr$_+j`@t1^+bt^bBX6D|$Gy=ODWEr{`URToENt6ZJPA8JmFCAU3N;qsKE=Q zI#)YvixRH3lDT6C)%<_7+wPk_*R)&xYK}LuR!vmG&fKyDrC5%huwBMZUpPN^gFFX` zxu5+t9vQ4aZ)RY}V$_69WUsdVCIs@)2h-pA|0*z`7$4+;Rx=o~b+Sc9ieU8Z|@UwDf5aH!f10xxJ;1OTTZTuA) zhi6lqa*8#YOluHxuuYA(M?zF5ngwMdHku=-dl^PBYPbN1Fq#1=8=1B_<}MtHV_L5Y zr{a#IY>;9;90rF|jK?USAZeBqYJv}v=vmO>_wd?9m;2#aPHs3y#f`CcmxAM zBc;L3X>>l7l6GNer@H0HKBhnrU2Q%OX_M!+pl!($!~CCLnSCNmk*{PJdQKrqCcUkkl={fZQ5r~$xyJvSmDlfK3iyMEuz!%08if+SXS>Rt|M7LS^$x%a!jc&35D zZ7i)qXKtH|WsH@A9#OIwD0}DM^DC#FqL114GO z#^!~oGZrPs%f?8011~L{>;oPEaE3B!3(pcMsFiz~rw!L$D!zD8Mi+7I%Gs1gI$+0j zA2XG@l$1-Hosj85NuA;l7L=d2lJ{D&R<&In1tNpV9{xv-aEZIr*^lCHMiw}LN&g&vn^Ht4_Mja&>cXmU3GBa;MxB5g)FuvC2QLNZ@LX94hz%av6A!2?3!l?fSmM)lFySrTxE|mXL zRb3QGUuo0+0Ob1G)tqW7-s(W|KB%=>;j2kQ!h4tlC$YC%LW{KZH*kcZ2zmG)?KHK= zPcJEv3?m4z$M`)&N3Bo4#b96ckcCD4*tA{9I73S5W@ewH%s}!fORFt8EMU)qSI@5b zl#*0}dqe24z{erSDW^w|(`(jlX~kDO`KUy4dVaRWNch55MY5xgN}6j-1%OX zBM8Al1s<7<1! zZ@cbsEgi~CPYf3b^yo7>@sj#E}^bYEV{Lm!+vrK`HOImTuFHU)~oAbth9TWm)bd zQc(6xs<(Z5BrSeLwH1Vb@9T)?zgUTpy63&%r#?BB3Zk8gI?J|n+q~1@=6;^3uROLm zvwl{`+sgfJhq|M`PwuK5Le)L+wa`xm1bTu^Oqm9NLD#v?k64|c>` z5hX6%dL1&s;9<1x zeTF!EafcP5yjPvlS}neX4Vz{U2l^9mGO-H2-4L$9-%WN`N8g9=NE#ulYfeERxl8uJ zXoYu2RC!U`sNE`R4Su8Qp2J zuh@j6!+y-$t@`hQCgU3!jte2}fXaSUcLMG?^zdU{L13QrBLUJZQ}-U#>diF<<#Y{$ z4}lZR$J)(Rt{-3hdSJPhc=R25so#g1Ox~9b- zju9EXDuq)@$6})=^@O`3TltNMcAGra;(*rJ_m{i*{B&SnC zPyjV9G9kXj7v$T%r}cYE2jqEjxbLh`(?1>1DQOc(rhVgYKQf`XjZ0k5*_gVEEZx=_ zL78F>jY`DTV}8dq{V795NrKBLp-(D`rdg%uUN1+4VRMlgR;$7J!?X(%s%z3GPsjWY zpP^btC$t_}XCcJFIzo0i5C3+Kh0}8Z3HO#h_LezR&uZ8T+gTR&_HQ=8`cnifiERD4 zbTzHQ7kT5GeD2ZJ^_M?pvUW5F*O&R8ysJK(2)Z-{ES#jSvvAkfNm3&b*bQH7_ zpp3T61)jNlwRrgd^i8sQH*#r=^HbLks=rn&2mnJm_Qf$@?bVk{k} z^^isl@$n}(>_gWzsr%EIytL1%2Q@kWEX^vsE_3P;^7vK~4!HzX0_JxBSJlcW`Ylqu z@k~UoIc^bq)N6~U3DKQcbiBc;8Y6B{?S1eN-uA?gWr;D4D($Ppb;u|=Z%-O&8yPzf z2mGeb733sn0($FK*Dy%eueNi`QSG~nG&3|gW$0NSWB-JX55i$T@hMN_GP{5DNJ!Bh zs2PH}Ce}vYOdrGrCI@?(mg$_xbqp70@t9-KTmb#e1OrS11Tc;G1(kQDnabsDCTaAQ zDFs%#r}`gkp!kN4NS|@TEbl}?I=8$6(Ss^V;KXpv)JuPC)n(Y3?FRPG_bgEL6lA57nA(* zXTxSe_^wKi&8*x7sCspvqAxW{<{_Tv0O+B*@&b*n);z4Mw+T_<%FC-wpHn2MeE zW>|bpf6m*re8wwwR>uv5g;R4+=qm~gw&CZaV-x1f`|&gR%CU9h1|}=2G^ieF1h&%l zj#Zr0pXN`4mJ#EPCo2JcNSBIRE?xyq0cCr2)Jlf#?k%MbSK>`ZSDa}Vs%CAiI{Y%4 z<4?6SuQ`3-x)*$mvO(gZ*D!dMnTAETIx%XS>x2YDbBDDwz&~wwN)tM)ur5L`!L=Aq@_T}p5vX%VjujgIt4q&?mkam$oVwlL#OQNyk za$0Y-6oo;YVyue$&wuxQi?J{I34fU=h-BYYwJSVGDPxdArZZaY88Il2x<>d7#0A56SA0HB zgiy(RM?|IkbG<1Z)s!zdkp`kAcP=9Pt4cCugs(srWzOT6U?`MaKn(Ko7Gcp@(jB>s z&kpU~I~@`f;=+_F(f61vYDlVJ2J%;UfGYrjx#BDzr*d{#qc$iGib4Q|=-jaR^+NpH zjH5`}=k?y57UjGBp&+k7XpdQ~O%-=?Rsxf46wRcS=~aj-0U*j?P=&Cbv+POoc*$Ig zuM^6|W@lEQuwQaqDP4KcStO|q7Kpm_b)%jb4=k+xV&!I(t?kLDl$X_)?tn}VTdL~) zR%wAvdiZ$hfj3xg=!Ct-gtq4{y;rHsFALIWlJB=~AXFBjTw0z{R1EGVgRTZnk-%zR zNN}=G7R?FS7k<0T*SCJJ=>V}|_cM>B`u8`YLQgNtcrsp@;aj{u?D+xT*5Cd)NNsX! z!k|n<>LNNK_U=!N`VK&i*f3YHd?XQAnre@h69_wV8*bj~3X}9P&*negIZtCDk~bxz z%zD!eG=v-HLQ}6n2CIwZN;3txAMkM&EM8~{{g=S?3jZ7D;w%YMevzhg} z3gC!~6-$Y;Pt?~j`2F1bgnALzBiTYc5Jdc44XJ!D6RYe~AoOEV&;4WX(Q+Wf|97x~^7H+^{YyQWc+J5}5)S^DhhXiy14#nx)o z6&dc6?Mm%+RU1bxv{6d(BdXEO%$K(*+t)&T+p+ zDIxVH^CM%9%aH7YCJ-OcEEH~U<0cz>G)%B>3O!5Ej!D$BrZY1g>xGODHH=Yb@~B3O zeO~0f;DV>Keo36lJXgT%zlE9_4CxxRmQXIS9p~2!Br`g}HSQk9EGzx#x=^NkDO*g4{#n^A4&>5H<~%dWiKNnY}{2PAm>^>FIt}3^Mop@MI&6Vw3DU77;NY@j1rU@9{d#6zGiT{zJ%m6nkn)P3)3q-sXf)tAs*5cx&n(Q9 z>MCs?FuF+I=(Cz-=hpv0rWd>#y6t47L6pfBMf!Aa%dQ^jxOkeVaXyj6t&B7q~5P&fhfs86VM32<6mYq2AV$s8AUylC%sp)Z9pH`z*6wuC{X@;FgE}KK5;5|UC}(i z_h12oVUc~r`PX2;blX90cRdw%KaumY7Xj)%h8>jY-@Mk4rq&Ym_M zuTX2l@^?utMIJ~-^vK8}R9y`s*dLh&5nd+)Re6^QI!!(YM+JZk1?{zHLl*B`-OeB6 z=JSXUO$O@z4^Y4}@dwi6TkmZ(ypg)pgtt!sjk#%^8$D?rROOT9&%V0sAuY+1c#viC zdi*QW_4npWwBnUM3-W+{;K&+P+V7FJTco_!+~A==&8p! zRP31#cC307owYzsHL=))aWwiDuIgSS6c<$Vsu;H9`lmU*hJ+fAcr`hvyS?>s~lKM5x6O70L*gy5^U*eZNpVTkZ--vQ+Zh%gcXD@L zj(;DcxzXII`^hgdOE)}B<;vP{9{{^cV6m<9a#KhE+tBKEzIBr31JWyc*iKN8WJ6fQ zVW)^6cu~W;*L0|w$LP+V)x_n3(PXy(<&bwy!f zIdGsKk++UCMFWZP4E>s&^qRuEFB8Lnu>@XFrko6&_y5v8W>z5dO{FdTw^qvl_KLGm z@UMd!8Ad+9UcXI0{8z68xECJvAzRXwT>=IR12m6iQI7qJ!~uiX06LHn{dn_I`+zG2 z6+p;*B1-@6AP}%Cyk14CD}n?->m{)LVNIHZ|LTB12k1aUjpIs002sk-av)enL}~rI zgB+lPKxeJHSENtC7@G=c-%H2DOV4_NbT|OG5G5w`{#!EN0L8lPl27rk4#++L9sI9o z|5vpCtF?fu{$D^~{9ix-!216USj&H{EUFhEu;z!KbhQi&bR2;)O757+6e#pQce+r- zWGCSjAn&>Gg$~xG^tA_z@>xkn3<34iS@lyd&Oi92WcWr-I}brMrwp#rFcfgy%7+>- zkhHvuRotlh?)Gc481EPaHw#_c@+d*P^63wiuGq!a)Wn-l-tSGQ~vQyf!s16wnITQ6TXSi3hicZqyJ9RJkg31D zJPi-jAv2%nq8xZBbZm}O-KQ=sS! zXfCULG;{%`@hLnGK7h-rer|NMzpQ25we0#1%eLh-?;Z>9A8%PpsZQJ_I15zNQ9`lPEBTTsHJh8 zL^Xo*%IHE|WP76U_c5}2lmv;5xnaDil@V=jM#ke2r+bf&TOH6(nG;N+W~gc$UsiXs zvf*rRGytD#+gF(!$SM!`^BRRU=omV!6m<*FHbXv^%l~%o9-i6u}c$@7~(&j)NMv1>a2$`e&YZ+L9|E;f3qW^qD z0*CgAK*%1RfcsE`qeoY13cfrxo=scubCSZnqi9=dzcp4LTiW$B;OVtR?&tgDIihMU zmz&Sr3TRZSS}wUKS&kPUXnx(3XQ1bS4AHO?H{=pC2owNAXY5|pA*ZzW3{+T92mjf1L3{0th+E-*{*!g?|Rt1@LPhlj>-Dn zt=etl406hMMP3`tS!jC9#zPo| z#mxmDsJ(W1-|7WQh?F$l#_+c@h%ln99tYqkK=~)#}6*tB}zEx_F|fmrqZd zek(oWmLtLJjM7sZ0N_4}<@Wn9?K7-dSDX8G+djgX)Qka*|-2{sM3VW>Zy zEg_B-9)FEh@j!Znpp&*cgAovN)B!D8uz+2Xz*qvy_XTu&u)C}rJ2Ps@i$)EPnoaYL zGr8gjI!PWC0;b~fbs<6sP8}wG=+_}^5CX;K>$iVHuRhKM;iv6a>VP~V{iN1FnHrfk zEyBI2yLOLRE6K5Ltq{eDnH#zL@u60rXhC`L;goq+aM9FoEB7u8bloi!b4_+kX)3+n?7zKHyBj0@A_k9N#~<`FL5ZwOSrTiS>*tFZGcN zu=SAF>|*>kdMP&eW}fCaUL;b-;JeFhGGBO(#vrUFq!Kd2;lD<0qp2&11Fzc*1m;cU z$d6%hAWsbmzSk1ReI63*RK&F@$HfpR^o`N>*-x!=!)n*=6!LwL6>kctJVfadjO5uE zJYf{Vr4fvux{hI`-n}>PTg6@-eCA{BPd{r%ic>f8xNrBtTVS{DKW2}Uc}_dW{f#sl zakeZY*O&T7*6cRT69GToVl``hOWYojYMoge!Cx{@b4&>Z9k>o|qEzMF7?%AW8O3F@ zrKq163foO6$hogDWzq`O2;$o9ZHv5BEB+*7*nDr?N4d%R2M>}TmOI|Zn~X>b?4MMs zS;u#$4NQ~&iUS0&7h!U(97jp@=V|S!Nq*Ct^V|~?eO_C-CC7fCmZWaagvFhWS^#wp zWEO9CvHE1+2T8hz>D)_atFD_K=STU`5*7!ru60V!C)L4xkNcy$3*Yj=z0=q@(p~7R zEYlbF{_}00MTUV{WWUpG1+eL@4|svKeByc|{YTI7?9gf^kC^OY z22z>xi_aLf41UZeCHAo}RVekk4==wWdOL?f-(Q8jruK4AZ?%+(=w7;AbJ#!`v5<<9 zj_uhwr|RiWgowK)kqfVJ%R5c>5sjRD`RKjE6mWi4gK$;_;^xr;N0zlLpYSs#U}Il+ zbl_6428^&;vzWdCr~UjUY8sKY>GEEX#y_}$e)lOFra$8O20VFhs$$zDgI+GTX`zqw zJ5YId)}PZBj=OsFZ1Rn2b8n5elcL$|TZU}jG|uaO=WSf$KB^o_lz4X3C;RWFg?{dY zhqHA^NoBBf*eW&4jqT00;@Km$&i4;u^TqeJPm-6GdcENOJQl@g1^TE|@mdQcK90~% z;lS?-bgYN9N+f$~M#;%3+me@Zr=APQ!eAgRj8yQ|E3_}sKwiUF+sbnN{oC@|56}yt zYN|rp$tVl`xER-6#Eth3I~GS8;CQm(c>Yr)F-@RsI1OWs>EyiUX*gz%B0^_gmyF$s zLs0y|M}(x!CJFkMf^HzVY##5_-2Hi1mZoHjGg4$5o-GwUsfX@u={WB_hh{TK-xkq; z6=CXyeZ*Tm-T~%$pW^%7JG`6leB)UUaogCqpF+RUE9CPn*;2Q9jFuprH5xh(XZxYx zfPqMIAS>3mzxKV*t{rMB=jC0|UM+vDh7@EnVl6Y~AVp+66reuSUi|GK!>9`w^cPof z=I8bZ20+)AH4agPedu6tGl(_z^-LcMZ^mW=E$7AW)do7l` z)6e+5*YAT&j+NV0%EnPnuwQElqKw#v?BdD*b#cxloW#x;`8-cO}FA}<{&#% z8C7(%&D{BGn&}yo0*axq*%Bjal{b3A9Vfp)YMDhD-1OQZ@PR$d?6Pil&A~#PmD!o+ zg6f+^*;kIXGmD&yLK4-gQ?zx%Qya*XUI+OtpB<9aqy&dWbkCb4@p~wrz1M3pBxH=eI8?hYYOh)K6ff}Y3}q5X zJm+F5NlMpk(%^{E`GpbNMu)utK4U2nnZEHp((ocbOA&dwLHy^{f3~|g8OE^sHyAJk z1>rI-nfS*we(H%H9%A2pz$j)vHB=61b`nz;{9$&PVH$@?Ap-&959;rI5ts=_X^_AP z#oeNO9o7X(%AO8*0DGpHpN3TFSLN&Sq!Ws1V5=!%6M;h`+p{lpNXT?+b&t4uH+*gU zdHumqUjeObR*j9w?;x3Q00G|p{3aF{!Rx=3$L*BfKqme8lieG=qA9}w4Q#{RWt7!q zZFRn9y?C1xbe}9INt6@NMFZ^Cm4IBJFjk-sG4hA)>`$)4R_E94wH9bQeZ(Z-Nd<9? zAd}CM>746ZpMRew0wZ0{PuPjgXt^8Cw?UbMMRs6+!5+k}=SkI3J92yUx+P*B zvqxHiF!S#=RC6Jd$q~b8fK=rlAovA@{NbcoaTZcuik7Mb^wjwY@r{FO$6CQv6N3I* z5>DD(_v8j7Zc=IK0mMwDc#NpXH!6H)kqCUjFO!k~om#g?B2u>BY|+9{)Gh64z4y`` zM;5@vd>E=}eISD0W&f;YJ2Iy!zGb}NbAOB!Q)UP~m;UTdDwk2yCstC*Uf_98;rpvl zt6T@>sHZtpcv^=<>2fq!P(iCv{=0uVnc}RIJ0_Z$?D*2{{dKbgZ8rG?Y}x$WUph>- zLasXvx9pX)T2YZHTtqdMnIN|BVc{Mx8xGwkWoMf=qW?ps=kVlN1E68Nf&p&VY9;*2 z!#pt10W@%uL{x0s;~tg|5#|LdN^@(-%MDufE*_kBMQ@ll{TaJ%*?xPoL8)C^HHGX$ zqKx04FW@q-JH9Q}$+}7(7WpMB5^h0jgVE$ZdwQ^l$2DDPUCxmxZ*^FOhWsD1V~_VH zi+F)LR6wS*9Ti1=4+`!et<1nb{?E+%bN^#C+c<(APf3g2jUJoA=0HUZSDiU-NR0Y6m0CbB%DZ(P;OOgCdBl{iRqon0dkJq%B`JSu%tlU;;odn<(_(9Bp>1=^*lVL>G>w=g{RHr^JnqOIa2;Fen~&0Z7v;B;c#xx zF3Zg zNDKUMhc`u2E&Z%Pukl0Ig{@~?zh2PDao3q|!d87gMh?OU9k@1UAxh~RR+GAFnXDC( zEq;9V-WX_fmeA;D1oO^x&B^r`XMSiyHECZow^J_^i+jfX%eq@UkKBQ>h|O1s7m?-I z3?faWdaB`meaj~DByPKTojZ~0qmy!n_YK8dwHjj`3NPLf>v7M%cZE|Cg6g>m#R@yA z{AbRZR<8IwFzHXe1!qBDo@S=xebYyO>`G*Rl_FG;95upVoo-t*X0c7Nq9}qnJUrZY zorDyXf9c*j`{Se6R9q|5Cz(tBUShA)$N3NX*~0d4bPDjuRC2mB$UU)O^&7)cLxBA` z-Cp(^!Mme=IT<29iB8|Qp3TUoa=}AhK&)lWCM33z>CV%ktqVx)m?Zji2+bIePKgOE zji|V1!lv`eqS2(qwCEP?X|@ZLmhqF@2~;d8z$dd{c^YlhCrN{wwtJY@(`PI<_?ZP5G|T10;HaOy{N zmX+O`ADi@*}J=oZ@izGE0oHSWm^= za&o1SLM=GJ!5nS}F9z(V5JeRaEJ8KA<5J+KttS!?h~dPMm`Lc>Q zi*F^nnYkf-ZM05c56F0PYvj>tnI~$_5mwoi^I_wB8F%~>8E7H=3x|%>O=5`;YrhX z;j(7lA2*^!K3VrRpBf(j{EB=9KS!-1mO(nsM|5LY+@H#H4WObT+du0GWuK-9nXvgh zN;$N&EK)2Q$gtY%>i&Lwli%%WXrzjtM}ut^;-)o>$J(~dHGR-woPR*dNQy~I|NSE0 zvC_eDN}YePqjrVKK=@3>o+VhWH{C*X54DeSxAXT%7j1(%tf{7~mvV^$kKHXS^~Bx% zGWXM|qJ4t+TvXM43jC^NMzN_(D@CZ>kV71P>bbFBF)9cAWD|*RvlQ*?5q0kXMMsvb zXT=G4_o{5wr_d5~Hym@|`spjJ98+ucPP_MEFBE-WkS7ahp1K#TQVS3ELwQ`rE5fiS zpBL3vv{4BG`P~cl`iVo2>9D_5izaL2<(ox}+^<{X$NHS6Z7v2+u?;uH^*yl-^Ci_% z+!!Q{y)($J+Z0BXP9h^bOgUqse5On}?oiq^Vuw*{q{=)GzE19I^Wz7Pd=MkKIgn5K z@!+L__mMtfA z)uIky#?BbYyxxPHveO(O;)%?I4hgmj6$ri)lHr8BNJ@5}@vcl|(DKAXTo(yg9WJW@ z9*(6DwH!3r_;&lu?_>47UY~*8YbT%?S5|%1SzaSXI@h!_Ra&8>9Uqct+Ki}WDVg%M zhRmBbwcPxjNz+J`<7RQ>!8=vk zSGNvdvfTE4Y}UO@E$j}z!7*(`tMK?(NTUinMEd)q_6Q6Stw?v6?~Q!yFm6opu!r{8 z)S_=s{@P}W;3h1cP4|qXckQ~_L?YW)ci=fdRlou#M|dy>=uprGo{iUVKuTc)>5jV1 za=+tp40M`R(x6k62W z`tUXC>7m^|P-kp!e*rDZVq3pPx_BLKkLNMt5d624POlCvM)+ODm5MS_lrv6orH*{S zJJ>dR@(04Tk4{pcPxr?6u~wf1L6=QY7x}?n6^YxOw*-eZwR&$j)c165|E{C%Q@l>v zkfK~@eBDC40K4~4EFw3ZXIaP~dVGlWHHPoCht6)f00;fhM}1k*Pc4|IboT{p%cS1y zFF?h@qVeq-Ah)1Y=MP#PwgdxS6+upw*f>#Ds@E}(i3O@HcYox%*KUCQNRYA7GYg{| z2MKBe>rsxKD6zUTedR@1Neh#P+#6np-dG)Z+%ns;+rTf+eEC9(#Bd-M`rRpy@p<8>@|E`&88;GLJ?F_$S-)>)Y}@QhrtM-7{!E&X>|Spw{r$W zw?oR;tUpP7Tn~_U3i^UdOqjlaf#(jI6V2TN>7n$bxf3_gd|h!&OxkVlDa~hdEZApe za73CcR_w*9q{4`wp;+-kqjd4ohBOu3(W<>_OYXoMZs`k7R)BSxAg5hza8w2)rM-Qv z)4)ih;%Pj#HPO4Nb(tdmlr3xwctg{ zMA>cM=p-Xx*A(!kBW3ipnJ(^;)8WfL)s$LG0l5zE9tKje(5*cFmaWNf6YVi6sY4$@ z+x7exG)fHG1XtZzbBn%}eFm-v-;+khe;B9KH)CrK>kA!jW~Tavpw%VfEQWu8nECCf zJU2w|oH04;sx^}%71likv{Bi;E^eGJ=TAEmB9%8-z8_s$F#2j^d=Du%CkQmfShNEP zTrc!k48if#?Y=c9Jqb1#i3dZv85KjU3L=Y%$Q8lSb~ zSbbfnj=!2-bKd=fzsJjJV?w-Uv(Fy$V-hu$$yR~)DPZ=4^a7LF_@c3ow%g^BbCp7~ z$$1akPd;ypGi3*EZ$E8Arg~M1s3Kwc6ShT@xUa` zAajL3zKn(0BknW?o*)Z@dh8?jQ0=KguY3qDw^U(C z>1u^!-STPNS`m?!2pKYhPUE^(zgJj!oELPo=`g`x*d9EVz9h?l#JwORCbcH94SNAgR+7N|7 zmNFl5E{I*it2um64Q^T8~letU4MeI0xQ6+ z0DA(8n|Z&opubL0Tp)`Z2qusmchL*9e*Nd?T89s#Jm=_lZqDbc0=>ou4HhlQiBZd& z!%x3=e`SmA?&b`P;Ddn4M`vG&X;?uyjWb=M38NdY{*=Cq1ylf4N(V2Z>{)*Yg-75m zYU1^E-n4>f6lpQb8ArLnZlS(R+*u|e9^T#1@9sonnRC%c!M@Z5x7s}&Y3n< zch=>tDkiv#5a&L5@Uy`~;CAgOD$(oi7_koGx_c7hw=yD>l~il85~JLckuss}>DYh4 z!R68|u zpYI*C17Db6OUa-&vcB;|D&%hU6LF}3&dN8jB$+@nnExS{{jkx*DgODlIfbjr3nIbP zM!*khD?kxD7rtRhL#U1!ocDbuGc&hm8;Z$x3zsV(ULry!Tr-25CwYASx^dMm4&}wB z;VqS=!IEZ9JS0ozUnUPvmD38`YlDQi5L{<1mxL{bxp@f9U_n^V#GEPj?0`EMuS;Y4H`5G^_FtYaEJ@@H`?^Op^j4S%wd|Lx z3JQ;c&)go@mIZhQM9DB<{9NF^q19kfQC}LvIpk%&8T+_AvQsbmPc;6Q8jRlvSIztt z_n2Sjxi>?8QN7U{mEBSr=qKvnA&I-_7=Y(?vDE2(p)L2|X~#0)8Z74TS6$MCVMPp+ z?vw0UjSdSrF$0_@-tK4D%|Cv#p4D_vw}+ROOp`NE-N)V#wH$L9AZ))&jmj?R(RX7q zxy^yklAe;2FrH>Lw}!Oey>!Iwsmtz(S^)bKT#YfNd^U#VdA7|Q*M_QS#7T^u+Y}O1HPtSe?yx5 z%)f;jc!~N)(C_r>8L|#AQD)#IRk@uWxQ=gGumxYN-?(pBp5LJyvPe{`bS&(}!0;}i z2bpK^K7=-HG;N;SRG6Kwm@8)W6c#x++-{d{K)XI3nrQ?21xY)C*`e2SbA`Y7Oq3jL z<2)PZPe4re4iycsruJkg#w@NgoPq5bqa9|_tUB}g(exkv;y*6ehOYQR>^m!34K2!i zHK^D=2}S0Aqu7O#vUyy>3X^*dk=P}kR}oE0J}^2rfbpNUuX0ujM9!~|YtRr99mr8& zLAkVM!22bulv?N}Hk*AJT-UsZJo!1M{WG+_`9|n~^j=WCbEV*xWa4;Ud+B=LpUwOc z0vRpR49#KMC$OQEH|!7rj^>!*?Eul;)a+VVqdQw>>;C)%?XhREJ0rR<$?1%~cCu~E zP^R7jx}$Vyh%lv;11-!IlS)y|`_9%RIxXP(M~sH~Poe}ep23H3TOG2Wc5soV8Xequ zCW+2i3(i81(W)$7ufyrxC3a)yEm%pWY^1_;Iw%jX8J8nJF2M7Q zpDEqzI$5mQ)_Zl_Nx8Fn=y;EEU|;p-?Ecmu4lnUFY{@Vh^le%9-C!-ef?1F?sdQN< zu#-JlaXdDx1ZOu)L#Z29?_`e&O!uX8_v^)Xk1FfrpS;F9EcQD=gQL;UcP$oODQQ2Z z>=JZyj#PUo#$=jL%)2}+p~j`GHQ~FMHB>>!aVDpL7OoooCcd0t^aUpU^?4?3Yq~T7 ze5NJek&@qOxi-78q!Q3nNItmt?4j?3MPg+`GbhQEv5iM5$KTxVWlom)B3nS%o>TSm zL@p6p{Sx*im$g=))L(Glx$n8tSARX)#or=RCD4wAwIkhvd|vIi2)t}x44O$dWsDOz zpK)5xBgy0z*C2LG&q}+#Ue_Ip^ncntGl2>GiVEetk59jU5wh8bH)oWG%K|RLs~Dga z2P$T>Lhsa!uTFvZvbM&^iSUz{T=p=9xRt#K2+%7`p-DSPPn)U_<~XwV->)*PEU@YtCXNEmJ#y~{Y+)`M zz6wa76Ige3JL=y3rNh3jCul=${J!iy_=&5>%qh)>F1{tSzN7P>*5q<2@5GK7g=_?# z(5&)PSRYmA_q`3%{1#aY%wFCwIC$@_gFFi?fWAw9edv?h(b-9wosDp8U2r2v3YPX)^!$#q6h&+r zoyMqj$?M(Si__Sa*mJCR(=jd*Gd%ljNh@w_XB@=xK6Xi5sN`W*2uP% zDPTa`D@|RcOIps++NoRlu3A>oRe{XYyW$KVLRf|U2P;m6IUivg>O0G65t1cm4_ycY zpeb(Vmc^2Z-I|ul{m4XfjJ_%=-HV0(yB;)BBBv^`B~$f)Ntac*3>9$Hfn!Zronpmy zdVs5+bLd<3ac^4p*@u8TY`ccvS|W7%7>;&W#Fa2kM|kepunI-i2Wi5`D;kx)Wj=U$ zY~i!WerU+Ey1c{cnbLiqzYyRo`k5UARf85jR@OJ+NppL8&Afb>D{$C{l7R85Tesv-yCEL>}Iq z<^8Ho1Qb$lItl(#F0h2 zdpy>7`{@ktxo*|{4G}EpfraMHLa_Hx3IArL&_u;6)9-nR+RxTS?qKWjAXZAdh3wUa zW+@sSUI}ebsOUxKEeEdIUuWfPnc%;1=i#+AXbJ$0`P2MK2a`IN|IFOt6iWs?>?ASW zw2TaqS_bSlL?FP&OK#>1x@oXjpgczCJRb=RjHv2fP>j`mLHFcHAv5c^E7808Ey-ej z4W8B$+~ntT$*=q+cl?bpCDZ3p`}5;dE{`HXTAc;7(B*u6Kzbjs<2P z&f9;b&HNe;3-(UT2wMI!Z9q!y4{XWwSUZmJsRVHeaNasTSb+;nZ_F9&)*pD_%F;sC z;XUjLi(+fEfUz}P)1!?#a4Gz)1Euqjx*yZmYgBztkUhnd2$WqEVs1X8CsbU2={=@i z7T8Z1&`bCjneZ#{*jijEaL^nxrNQ_iu*0TYJLl3-V`hsq(Y4s=@tuIYMTcOJe9J7G?PAERUEI3hlr)eg~{H6V@3RLkH1stN}*DfxSp_rlU6IS zWBH7Ms0)n=7$u6ZJFbn>KV$7jM#s%?tiNCHp zWN--gbNk=ye;c1Txh{P)8?5mdu&sIF8LBa^J$+bLy%)ZNOK(a{LO@`#S%N)hEl)`3 z5ibAdLRSyjh~|edF5#3{PX)i~)TT|;NjX2N!*XgNn^Nq+<{_vVQ$GqK;I|?6xk$^< z24}n(8(%%#wJMw7*L4QR#W4%2#z`v>*>>Dh)Gf8Q!Yu1#X>_$rh$rnFz5@i)e0u9NbXt7)Gct%PxS0V$#d@HK$WfkK0g@1QU za<6$^cX$zAqF|IFRP!kDW&fb9=;kacf+-ytg_gP1Vx^Guee6-=kIW61pJ-Ms%4)1* z9qcBzTWuc4^wF#)o!qg*gV17Mtw2Hc0Np~?5_@2Q1?kDg==nyM;l3>PO_S-%e(XZm z1LW*LeLZRmF)mcIyN^HjVKxf2(fv{tl)AG@8_LEu)14kJXImmQ05bbl2j9OiM0lf6*&$&_4g5z z&g#YP`h_IT8+ez)m$6&+!MCu~$*rBX2r$CkA^|P{cH_EOxpqseuQcqh`&2g$e%Enp zvjvvigkx3Xc@f3|^?TfC4IO2%ujO!k;}WH+R^&*%`tV}hk*E_PhI}>CA4H?&t(3%9 z{N$)DOHb-qsSmYSD<_X+eY7Wz-6($k@NCI(>B~UWAaoyqWFO0LhB(=?b(SmPu(jRM zn^iKpDpM8pBi$LfPBm65+;qCgy_vH^1D4X{BI2W_AJq7wzy=AiVSI3Qq`NcbEX_WR zSxnY1n0BeEYfGRkijC07eQ^{vO(FMZDO1BONd4B}kHfTh8{%X7(In=U(~p}GsaG8S z=PJKti(mAk{qDzb<)0j@dvhyRHw3nKrNU&eGzM;2#3@Rn`HR5SQDo?}H%~|#iQ4M2|9d|Nwq^ggMQT@$4 zJN4`%KC-l2l5aJ%$6z3?QQ%tk54uaj=Pd3G7`lijmJexhy+r>7M+WZA0 zVJArFmsqX}@RY4nB09;_|gW;kR=F&SX7!54rm7wH#bM`it>f&k7)&4in zeJkGcKdL}?qXZXjcEac~-zaJo)Y};JA2iye-Z}`kHF^ew^UEr?p{_kWA((0Oez4Do zH2E2;RE7-OgbD*8{LKQ4v%ehyyTNCx&gMJ zUHz?7Y8!E+Pf^W?kO=Y#g@ku3$ zTh4M33Expbzi#`c)|9_2Y~D|RMA++Qu_+(Q1y3cu1`ec7&mrA{`|9|Gc>Vad^^ilQG0YJVb3LmAdo5wZ!^1Ga8*Fck_`VRI@w<$kc$)I!y5MfM8?n=X!840}e|$~n z;BpSW&!0smvb*{5dBm^lL86ypWwzw~sPy;1+QV(}rCM`dr`t%f?O5Y;)K`|u!s^St zX7Ih0%2 zpLLM8z8orq|FCzC3;84stDF@ou}sZkJN))WuFoEX6&C9NPxm{3@x+n=G3c&XM+;(a z@>N}4xFKvRc0}`45)cwwTu}VESRiI9Q0f2~4h9&f$mx(ce1RaVilvdr;~bM;{*G4( zVvMV|hWm|SMZKE+jzPtM#yofSyv2u@7&$0r1zG1lYRL+)rV-&xgko&UlDtI7tRN+6 z5a2Xi-u}lWMV$Pr;|AV=)e=5bdSs3}iafRqdff<~=m*cDFD!Mdfp9^+wK+RwyEMy5MC|0*AfshtR|Hi@Rk2)GZi5mHz|1Jl z=i}u{bA0@0mBf1kM@OO=G%8QTS_{t9B+Pu1iP`u5hd#}9gFvm?yV0;Yf`03Mo3;R2 z9Y{^~^Y3}qun7K{1kRbzx$>O*d2Mz|U0fH@>=WHoghN#@_IRbnu(Kv0#5?wbqQ8C@ z!T5Pmb#K(0gZtxV^G`(wV)eO$Aw~@Z2}Pore2>WPs4B2F1Z~9G6>B2e!;ZF`iUH1! zpEYs+yj}=qd-wRRk1>!1_|thNuI+-VGX@sy}~)n zwHWZypf((OQSe5B%9({RozFo##gWLfCJp|99S$b^HZN>`W?vpvNfHrj;TxG?9(TkB7j(NSR2u?3Sly68@c%kLMqeB zI_D~Fb^kCbCBaMDhijcNy-G82uHva9m#(*hD9vB-sO9mqkA$)oMGyn`Dg_S|My~IW zbl@fWq)fl7BMv#92n&M>E$0!bF924v>zmJfpKiRkkJFJ&%9H9==dzO!GQM9)nSdEn zzYv9DM7JDc5+2`e+NGeX0C` z;nBZvs8sP;-)1q!!1M=3kcW_} z4C4UfXDW#6Ug|62)irIv+n&eZhFaW;eD19lOlm%k198<2H4sW^&I8%@IPryX7S6KT zCWm`G#;)*;*Hrx-TCIhSBYI7CTJKzD;$xHWK6?)uGI?~3Z<+E>$SdJ%0gjIj5x{x41; z)|dHc&Tuh4&4hbf2r;7-0b`6{9J~FEStN7O^NAxmM>JTQrIi)U84KR|%UA!Ha3ONI zK-8V6x`qyVGeO5y8+Jd$Rb#VKBHhMc3`~Mri>}BB7^l@Tp3}1zap|*3)!{xqKN99* zR~p7=lY+=FHZU+owDmlVl~8P9_xfck04|F-e2?E%Xl>s`iOZ0>)GO)^M8NIbq#WEA zMv}qS-i4dTX-~=tuvL{;Sj5~fpnJ!w6fjXcdVqBEHE~yB2&xyD?FS+2^Ps9jMv;m~ zU>Z&HPSA_3`e@8SDk-W?@0GmuIhk!t+Dj!OX{Rp7m+j6I}VF6 z%UUY_xnG~4RT$0oc%im#^7^6k)NM(kkdfA(QfiL_qShVJ)B7+{L*$Y*hgx#ngq>uJ zr|Mz&`Vy+Ppf!0ElSrrol~FX<>EpF&vfg8qEkdAYc!1XUcDKoI?>XX04EpCcqCr7; zs(AH2C~Ex_QO(-bjx0YB#_ai7PwZlRF7wx`N9IAFQLe_YOctUB6wl z+l7KBsgN6Mfo*ZJEK*vT*VV_A^I0)6L@Yc^q{Oea`H~OipWlfJ2l%fPa6k82#$dlz zxZ+Z-(NswV<=aBv_oYqRPo*8U=N&=*YHHeiC+|9p|Ar&6K7V7)UIoGGtJ8t~lQ;tx zLRc3<8suCKSmkuTZ|HmF-%&jPIrzg>aamg7y^d?Dh!G};6y}Uam938T7|R~Cs5A*R zU9v+cn#vw$wdv6sW}JISR8KUP?k7aXlk|XoY7R@(K9teunoOD#f`RN$BxCa2B7i35 z+zp!z-(If?KJEk+zPY3-|M}Fky~$r#C)@TWN&}Yb39CXzKR! zaWPjK^6jTYVEQ4HAilU*p?0c-@+2mjikh`BlTo@Z8F zP#zfTQy$37bI_V1gB~bPEY0$Sr?`kp3z24yRxM^cql*nsb!6Y?*9iEz~KOi43+9%0>3$$CoPYlNl_1Clz6 zJhSeD##OAKeW0#?lDqPOZ-5>(z4W8+C;76(*|r_JTM1AR%+vcryzU=UXK9Mtf12(;#^NcZ>R-)AUcgrbB|s)V7AuE(DDlR8 zn;4Q=vL2t<$H^4zz7vBF{1f#TzU>_m^nKdLVS`i}WY4IcrH>d-EQiYp-wen0Yp{-t zo7!Ader)N>K8kFkkvyE_4xy8kAlgHdS>8ajlasT4^PyeRp5)ttG-LYj`8RXSF2Bk~ zq1ZP}X|lpwd9m9WFCrfAhpZH~Fxe@RZ~u@UJG%f6dNFFjMvpV>8F%K$>C&l}deYPr z5oJuAEOkDF(N?5+A0HaBwHI7eV^;qOukSLl;pe>d?$7o`>;_P~j(j4~N_X z=iR*1k2CHO_|E28e7!KoDLbQI&B%g;T>m1J!(4n||G;Nlrn-1iCI3>OY_KHaa>E*c5(w1N(ZjkKo4dx$NDLD3p;eHn@yH$zCz7ev~7+ z>+|bn#&@Co>h)*kgzXo6yFT5=bP+`S%)E`hF};Q2n4(sA{P6Zyy_77%RfX1q0AknA zM}v>3k9;%7iY9%I?^$IYzt$qGNk5fPUj+TR%EX$Zer=H^;1*_w*C>ho)0FCVe%V}< zx@xol|84hHThJ{zk{CJ+c{N_R z7kuVLkkQgd6F*buAH893;s^aF7^B&_TUfEABavW(y3d56inJ2k23F6KfgdOA`}ByC zzGm%irNHE7x6@c6iDiTatTLHei~jjrw#WJQ2-ioW8a0dfsa=tgv-+OnDxPG9D)M=LZZnFSZEyKpvV{)4c^u+ zt2I?QU7ywWP+fT{8f7mx9hSys*~>M_tP&N&kg7lm(>2lax>mx{s)#nB@@9;&A{5se z>z*v>Yetj!s6^4kT=*@dUv0uOmE;?HTl3(~^r3{+#XQLX6tCh7uY-0oX9gM612lcq z8sDd0Ez?3{U_*6}Q_1=PZM+q)rh6`j@@=axInbGHMhc}zbq?%GC1Os>EW<5<#9Pp< zsF8Z{;eU!=x&FRqJKhTW7$57@Sy#}IF=G)sXXf!aEx$j-f@TMe3HEvnohf_jWNaF# z9S07C&5pp(!7zV1eQF?}6RPZaI|vukQ?_d?JTo?z4Eg%IA4WHa^S)9|{qnD$`I)LT zxtb{9#^5Hdr-okvonh7%;2j-;jwW4MoxhPXGQQ@y!HbaNx<9B)d}ar z@9a_MPBlP+i`Kw&o)sU-T!Kn5%{ER$WEnf{G5ho9K*k^>v7Gu#7uAsVGA-T|JV~u{ zBmNmIeT1<*rZW4r{_oi4dO#y$XsOHlOvrJrFA^*=T;TYFOr4-(#7_qxER`?+dM>dW z({5f}wib{_IiR=plc5kHRMCVoCCI+$HkoVsWQP%u6wu}*`DRX&x9PH94D5a>HAgGt z0|^$OZA7Uempj3_KJ?0DhLQbT#K732XXJCzfNj5hV>583MUA5K=#AM+Fl=qUR(`xt z?xJTX?jWA~jp+B@9N`B=rZVd&c~2Bt#;5o)9ez;0e!L!jy74&{y`r-~ChL7b_s$B{-+DNB)$nwnx2jdc%EP-bj?@ zS;&k1(=udUa6AV{Nt9gXGH5c-6-ovEmdHf@4vK=q{_#wCwJMuM_<#2)NPTj8*?8l4*P_gF1rhB9hs};%G%HeLG`;lX@V7j{b=fao_Y>{mN5E7irt%~jjtU0+uh{Na zMwNXBs!2r)#nR4NXZ}37cgsWP;e8kWlh59K!qysx%B{ombq6lqlxqGj5YC3JkCy06 zPeBS=Puq=8ME`IUn;H?(oNV$LL zIp>PBqu)3ces+*-8!ufbYz?om)z4m;2~M7)4ag}60F)*5+1{~DVaO;=L4_vP2%a4;I3de<3$~g1V z<_KNBTJpGLSj|`zxw#qSg~!h3CVsSdn+87lB*W~07QNQzh3L@2jgzqt6&-vaS5P-E z@4{qX#=@Ls1Z&(xRXWoYTjO`t;rc+y)X%tM+u&ccjj0FCU;%XxQr*o+`Yf3#yCeLJSJyF_1Fhy5sD)aUSEIFu64SxrtJifh*ZBf)M%hVmS!pv^}%4}(sb7FFS z8jE1dZo>SW{+IT>D8;~iM6nRod|r3nK{N>RCDQ-Q>jLBP|P%p5PThN^J3>gmf_Nr(I3UK$m+ zoWpF|42Zs@9)B)Xc8@v1@eV>S6~ceA`$;_+U6_pd6PXt@(3|P^nZCxlj0yn*ob9+{ zuR5vZ&`_2cT7REno0X7$3vcBrPdUQAlV?}0iCMazdV`|a+G@vV_i%Ilu`%6=xY|kE zPKh&|X9)MpEstOAHDtcg!2w<8Im1U&sV;~_EVxx+;;YJVSqr&fdrt&hgq z^Z3hll*(jtr*=}!c!o7_bKOEr@vmKd1N*vK_>$qD`-0dsH7Z$#od`1JlRh@A4{G6- z+kJce{d{fA_Ur``!c9{0lVRt1me!+3NPRbx465B6u$(W2P4f&u5=4#}5p&(jbZ)A( zEH)Z|PQ_=p?OBd$TRnx5*mFv1sf|Sc2omX(z9`3s3#yXGRtIjztxRsbtY0=O8mcpT z2sCzeyK+y;Av`yC6YEcrBz2epw<{3TsfLlMmZ*AM_-%^6Mm%m{AL!~yBl3ZY?mgFA zWugKpPdZEY^0eZ{JF7X)F}a?CF!I{70&zl4Xpg!p!jw5mS`@-~6^oig5F}OO^^r_L z=Ilc-IwK!rj)|oS{z~WZ_!O1n6O$Ce*m`9m)CNX&C!yXxT~}KQH6mIK7qBDS$LQP} zc8HOZF5{IN199t;(Q$x!c;CX#cjy&S#Q$oJJ=x^}sYMAPRYujg)z?mXeRa&IfL#mi zqLBK6-W@eIb#H7<_pN?6>jo==u^oiWT#66(8XB!Kj5*Roiw!I;*FHxA?iu|#$$ufc zVvG$gCOp=cf4IKl^Z#0Gd6DG@z;7Hr6dg(;(JvL|2TRZ0IYmRdJ!^u;2f%RCpQ@MqriysX91$d@+DT@nKre9XVr;YT;i zi`_m?ZO%HQMxX$_o`LpM#1fu)=gUXiPyg-(z~dH>Z3qhUzY(Udsu9sh#`j3C?RyfB z49Dd3k(Jk3b%6X?N=V#0!MsH&zVl>}Wt8cgeQK*V$elWM25gQxfhC;PFr?uB2KECa z&aSxi_0f<&t8vg=67FP9Q1bp#m>(lOVS${RjMa1p$Qk=R!q}KM@Rs^6kEwrQ8^A=7 zpx8nzii#aPzP%{0(v;rC$ze&663&=kImUP1JjMFx?xKGb+s+#vsBHO#|VX?jJ&ux;^LtFk0}JImy9tf`0sP0 zG)}c$`;To;@DPhbieYBnd2Zw&$p7QYv@n}3l`j^&OQ@sw{v@7)JrC=#BSn(6a@tls zw#D9{XoVw~lv?#nvsH*uh%e@qf;F=Q#VJ(G$culVNeO=k$ZD~wa zkuFMT?t=?=ALy2tOok0K7+hvM9B;a1TCXdq+}_X|O{q_=R)3a62<#0F$X^%i%put^ zowH%A71!3o!`?^{I_GkNGLBKITnerzZ5-&dckmrO%-^K=o;}|>oU~AzhVPt^c@RMU z;`vjPbC*77Fd;c8CPUD~qt8ss7_-cd{!F{qpBLA17k-ikwbK*0U(NMC+NCtS>j$6- zfCH|4+%TL(mED|1x%5^EuR1WoiQ%{7eN<0Zot|1Fn%VoR4{!}7h!*_8VBmzvO`3NX zkLm@L`42h2*Q-5z+}8n`WKi=ZT2dd{ad1(t_)HM+7JDX?bF8ydpR+u3VA_;M1lVE5 z2Sv34#@#{vO&4daR&7Aq<$LD0IX;fO<_+O>2=Z$cP_zqGYBD6Ki^qmhx>zte1|*fF zBa8jxP0cZp`b_A}CF2);La}<5@-7y_HlAdG>Ds+oA1J5 znlDwjnZ7{&HH#pjZDAoCQ14B}o`M4%bmP}F{t)A9rUy}5=I3&E;@8nZr~2<_1IT_- z|3%a9`OHUZb_l|$E#D#<>W$KbdNJ(NTRLr?d=iDCAsAsHjDLMdk^i+v_#|vLpE@I? z!tmIt-~R#r!(tmx_DzLT7|!V79aIqiO%cA2j4W6{c!-aA7BW#%&$?s&ius@S{PQ|8 zyuV}P3B5nYs=)C)La?TSM~G(HHpHxzFk-|IMM>v3SJZ{`X8e>wqauI zow^IcbarN}WKmmw3lshgx`Ul2wozmkWQ^gW`TRc~hXMrHzm5!cut!e>hs|&gsd?@E zL%mUU)-2SU{BP2Qg7!ct`ri^_mIH(ZQKIOicFIgg|BodK@rgl0bFO>n#IO+n}9Us=?ChrXc}-nDxK9ut30UHlA5TB_pbcX(e%XO;hRxuk#X0{Y0|-EI+}1zXco zy_UwZ?9gqxh(C6Gj5bs_0|?v{L{Bd@wy}(XDrT`A=^RZjpJiD~L2PP-@J(w}gNfxm ze{hQ8|GBXen9ZybJ}KkDBAbF$MfIlC2+5kHgd%A&#w(i1pVv7<)c~h!DkvK9gUgU3 zY~pkv@D0Kl-rBc&=FfQRU3;u=o9^eCEqsb{xo1fP0p5w+RVRmVD9gle;a^SaLQZB3 z+zt+BHUN3((9C=zM)O>|q^-V8)tCQB-3bGx77CL>VG-n!6W?YuYqzzRl@P}Q^CJwX zw1u5d3vB+LAJsXWRR&9$xY}q;2*%$Wph+V@Q9eT!co0x_s6KY8HX{!xWM9TCW9p{2rqSokQwNk6uOzc4jWkD*ml9 zLiys*vyDLt_?nPy-jasro#jZM^?bZ`2fjS)tJ$=60H}$vhyqtO1DbVrJmO-z{ke(ot}?Nhn+ZJ ziFxwzEJ2U0&shQx#I-aw8r*S+&|<&GbHDtu|Lylp8eI@Js0YQXYJ9>4y7V(3!ayii$o@m_sdrPMmGY+%SGM=?znAgNf` zrX}3ba#LIr?R0YvxYfd*aCx@6cJywS3$M9jAV!9r65i||%V2xX*aJqMLC|Mf?RZP| z!t?i2asFe0$?QLqF3 zUl~qN_n|@9hf}6PkGfu83h+11E>%XSS5t!GDew4}iTq#XRq+_rhd)1J{*4>H zM|VH%m5)L;6;AlSdhZ8)2h2Eo{I?Wg|B|j4B&Ib9*=^^o3A9NxrQYl9K7zufF2nK~ z>3*vwSr#-z;^E&_w?FDkVy7x;72xpPya`o6?E6Dlp-r1LkS4|jj%ea9U>=R{KmRJUkV+X(WOSpg&Mr&#N$q?%vIyF<-Wl(>UHskG zFTQl=KKZsZF4kKS4P}{`JCn)L zL^9lU<{Ss{&Qa<^A`ezCVK8btNt~8_k7M=LI~?r7E-`a#@j-uMY#VrmugHVfZW(j@ zvt_r`>bv0g>#7S5FY?%(FUjlHSM4XyS*9sgy+fY`8b1t_JUbn+Y;}O#E`(|04`9lk zENO-_1+uJs0(KbupUmj89-ry=#?rE^HEAUYrh&`{Sw8@hF)s9{$jctPA3oXRU*t}U zqg;O0KyJL2?Ct%W?6TIVKfYfDNt-UZ3;)sw!H(4Mc~4rfF`ixA-r`&Wq0y!$rPyjV zDBjHa!{kH8r^h8&WYlz_j~F{Wq3hwiB3Pmo7+PX`^(&EImePXtpGu{F+=($7i~|E% zKO^=l?|Zjt)k?kZkS(2;iS^PO@evTz9Se$@{V=sqe6FcQx!* ztKQIS<|7r=uTnT|7R!^2=t^W4F5UALvWZiB(;Hp`fwg*jVI&h#`YpqNzgYIPLTNUmg_Yx4)*%U~~U0 zeY4O|u?tj43X;KR{GhhF&|^Y1w|h1?G^5705zO~MyUjVS2nX_>aew@Qdn&70twKN| z+Bdx((O7k``j+>TTVn!1n5+y*ZkKER)&99SeW6q>TLxzoT7G8=#J2D&W@bH@$rvMQ zqovXEJmxp%qjTvy{83z{I?yR9C+JOY^4KoQKs2A1vT(WChDlkWFTe&r%Bi>5Mxf$WMu)C>f?gC9c=dTdDF6~H|adB_w^ zQ&@_;R^1BS+Zq(pcC0F&`Lj2p131iLCb%#`k$p|>Ol+6dRJ_nAnU^N{CMNH6A@~m+ac6jU=+XM7t8P<%}uZv$D*WE)S$N!v>P6d;5^1Qz*Q9Y zayH`}FewYbKcw0LD!D0&JSc8&peVKm-uo~dRkmMm7q8|DhxnEDb2Vg zdn2&)$e^`{#ivq%@y@f{t|I=t7g42bigjN#cA`sda&5V-pY*#h>Bb1&AFOxj zi0Y)MbXtcb3m4B^aWcCb{;x%GtoxXYP^PKH(F6yFOND0q%cekN^v&;>HJDa;?;7h6 zX>|BuY)XEGQk|-Lo_hBYMiEjSzvt&nm9I61H?@*91YKg;H5F##&8%7p^|QXCy4>Yv zs{O}J7b14v0Fe>Dbtx@n5ULho&h{dxj*lB<*W1(qJIi?^4!aam!<1+oGBoZ|!Pc<5 zh+i=*YX5S>dH3l1k>PRb+3XoMAybICjnm7yQ4N=Em(nq^Z2g+OY{))cX;h|8{f!g5 zBl!F1%ef>h95CbQd7^7J2^YkhO16dExGEeslFHbiGAm$Aah-Gy=bPSecps-c`c)aw z>8{?`$X{6XjxIRk5V^Dw$4Df?$G_3sYm0iqy^9Y%NyB zmPVeCSml|6w7M=>JjHGy6ph7#5sR5kFyA~yv!zL1{{4>G(craqz40##*XseVTi3aW zvU1VPbGQU52E%b}OB`1E=87!lbSfEr%lk?6qsuyN+cU=8vTbn9Z=J9qfFPY3D&@n| zC){qI%8W^3X}(vpttP?x5t}l8M>8BH250bt@#l~_imO#S;z&I{zrR2VV}hnEA=^6H`E7(x zbF-DUbuCMfv^j}=Pu0tpHfkswM;3OD4$=y&<`OY!&EAYNJx*bV zhSsTe=eBs>7T^9ZoUp=+wF2jt%Cv}f3{}jY)oUMz$Yqu$@@JkrseDCSaZtDN?;)twv>ArJ%e11Feh4)!7`<#_IETUsuC)Yv>9zB5DYq#W%o6G8piYYQ*WD>%xnnebOV19id3)F<~ptz%8ksu0Be) z@{_V#UY`%}@pm6X@2A^HsS!@0Xxnn?f3f!$(Q!1(7AP!>87#6GEoQKoC0oqQ%*-r{ zEtV{1X0(`@(PC!C7E=pe``q)vUF-ixZ}J9r(t}=Am01y)5m~W!M3lt9`3NS}MsV_C z5c92a{BjcLE$$bnO*rxO$2??2032WZka%{tP>k^Wf% zDExnxz&>9Rt2Q_!v1XDw#`6ynsYI4wZp4SL^xT9(#2Q!7Xg zGI|glc%%RsTCs{|>UXjs4e4Aepeik{s$`*Kf1R_PU_ z&~(s??}m?X61@_1+O-49?of<7H!!H&X16%;v%E_$Q4K69AyRRS{jv9A%Bc| z2cv+uo(!+SB0kG8txveA@2SRzV5>ReM*Jgn4rTWy6?79H)r4Sb1Knk-ZLIcP{>wX z#ZP5Tf3@O;n`XJ2&2qL~Z>o2Frt%TD*}jiVJd5uVqZ598of6P>Ipg^uQogv&=lZL? zbgc_J>Sbw61`d)KRsCh9>oT&Vl$vZoc5D*joAb0aZ4$%5O;xYN%Nxm}&L_8dxF>No z*sV4cH;tF=cYG!mjtX53dft1lSC!ztt{0$Yt))(0&z|eXkq-`rKq`#MwF@j*zsqWT1n}wiT+NDC%Dm!G0Exzd1BqwJPCwqZD1)u%hbbOXBfmYg8 z;64JzgSty_y4LC83EE3^a@{EjBtw+ub?quX1-QLbK!oU+*$FDR*aJRyQQ2Gxst{A& z>XFT1QB=wKjU26*?T8%SQbJdjtJb+Z46{Y0mv6<}YzljQb#h)V5O%QDYE%OvrprABg0P8mv$#uK9TiAO8h zoy@kaw#7wLCwa?Qqw(oRZ{a1JczhNjZ=5xaLTU5W`?$($+XH48TA9bgH2T8uLPV}T zLC%XkeL`~L=Qf3Ou%aQ|kpp81*8!fyZ5E@d6m8vwa%0I(*a{D>6EB@G8Kc-M>*;Go zg`-&mTFgp^7cE+D=dA<1a~!mOMLMC#vpxC6M@Pq-k9yh>h+Y0Eo#qNG zg(8$Q6T@R?JSItD(1gWDQgeD_t_5Pp@LlTy_D2sCdZj;?$NDeRk~Go%#5}FB{*ytG zW`?DC=vDdRxUF47w*;Cr^XIj4PU8;wn8r?GUl z2#toQ-bkYUIDdmSVTsVVyaRPz@_@c%!cqH8F2vb__Y(%~8N1oigi{z_DBlg-ZK#jC z(f*#8S$|j7&eL+wZZU>MI6+_vxi~QNP*>~W!mRL)Y1dxU0WDz*_`yE`ubh66{$typ zP$=BSbmnb$g95c#(mo<%xRe^-hwI5tEaWMKeN|y;lDLe<>dwUXV2V>{0J;83lHWvr zfhA-@Xqs;~6u9qqh68gyR`i@4S4I#zSzBV-yjGM3DVrZcoBHCMt zBD)FL%YiZ>^ zi-I_|aUPDQN|o=dJ6E$#RIVCNa&14p!L%?244#lf8+fMG*GQuvQJ)W--nJ(h^en(fYfRIp~n(DmONRS@coG@6| zRYRyPLN73v|K<&#!6wnSTvgA4-x)^bBe>Acwjh#Yffx~=mh0AFGUAW^mxGy7h zXHXYhC{s81#&pFxgV38ckM(PhfU}^$W6_s?3jNTO1yt1Ox3ArAD$Q|=#CpV?Sh$x4 zlO)?_MY%l;O4|ZzX0g~1)O3n7-w1WR;;knc+}{&JJiQun_eO-EKsIR)XKQ>`nTgaa zEOPI+8eR;FiJ2ejk)f5}-T5HZXL2(9F9?J#YI20&`4Yw`7m+C#)Ps54wts3aJ7|`& zFe~jvv|9!VygX{Lv_N5W4A3qPG&_E=nO3x1JynVyl39oJBi~m~naPkl`h7J!>R1%R z(?rY!NA9+AO$t?JCXjrhYbTZQs*OwQdoXqCSNbj**Cr7WZ8Z(_OsDSfk}W=x6?b_D z@sa|>z>IOzmoh&Ea87-Tz;?FrOvMf^VtCdC)rx<$a@(hi92G~WxSs|v^+)onl=9Xs zV{Xqcx3?4jl#x+CY4nndI$ZGc#2ICEZq^U1)k8n6H=6p+ktP7})bnIMTpNg}I&`?C zV->NK@)-nsT6%FMriA<;0NV{eYjSDsg7qu4fiULs$Fz*DcUz2Pa-a{YLeJ-qo8M}Z zNGS+{lqoqE^#Re||M`nL$&w;7*c}%Ms(gQZ4Kzzua z7yG!i8lM=)52E|HnBIE#*>agO0A&2aXJLI7-TSu!uA%hrbjIuVI=6gv*akbh?TuZH zxrCeDvZQJ|E4%n9TGFY&xHT-=gTic^(gapNX8W88CWYZ5pc|g3|O1g@!8PCfGQ!eMY zZH&H)7+o@6WqX~D@_qFS0N2NAU?CvVc=<7xAB*d}HtOyDb!v6siO;vm!0|)r2%u}K ztt)rbmdK;>mbfdkL(pvUT}UHkt&ci=xl(HEHPuIl^^R*99{dgU6Ymx6J^#~4M&}5F zKlai&R=g$w3YU>S5m7k=Ms777);FqeN&?3tRj-7?nNIEIm(qu}qfSQMOQqqTz_ajC zDXHhT9>@%{1)dyndq~k!*W!6`WR4b9TI8zs3a<63U2D?=B<2Zc`K+(4J@en0`fQ)i zQ-n@f-2LV(UmF$2bEw8Y&5fzo+SyAVi~;TnHD1!8EEao-7MMh?(E0lUKiAxs+og&n zXPu`h5y={%WBNm(Fr>RaoesLV&hqpjbW>W)1bqGqXL0XbS<`i@c%^OH^W)9*BA6+` zF>19W_>pMAjv27nnW;rftF;h*H!t5-Z*fw{v^)f;MhsxvGZf`UYgX+&f!#+`H_Czv zVC%0E63kM5+LsOIM1r(%-LG)!GJr=%<(V*GN#{uQ`W?2K8k+b;&YOP z{}ls@dVF9)83IK;wnI}*(88BTk+kN^pcyPE!KOvPxL|*hl7*ay-leCmAAaZFKbSGk zR-Vwj4)hQ@>daWxN(v-j`)Mf2+{b=Mr(>a7a_Fusc-r58 zPp9<>4fV{qsoTAgwH$wsM0Yws-x203=bqh4J1DTc(u{xwJ ziw+yzd@MfxZa5_14YJFtzjjAJx8gDS$H4t`qY_p>Lo~oD~DD zBq6dIheh4qY=KhsR!)`eW`^S1Pxpn~LCQ3)1BfsNncorCMCP-VtYfmH)2p=C?HO1U zX_`O3JtDwp!o=#1Gc7l}va3K`nZ@F4(nTb!-(s7xWlr9)wOOR*1t-*g_+lYmoSelR zj&q;O=6&r7qgi8!w4E8G&Q|OQJy0RL>Xt|v{)JfKc;&zZBcJ>d{C5`E8~67eP{ibU z4iNR_txWWZGsWOMVnI8hHbbG#mk`kxS<9~HMzxj7s$qy>HjOCn&sUuFg?n<+9DSc2gMzA`GcI!nXvJiUKKSAdd|EUezO(;5ior7b%v z(y~EFBqnx}WQd;Pzg$ObZFC7f1&8~jX3!DnY_ZO!8J>ExVhgP1y}>xw

Apv{JQh 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 hS<2K(kjAGJZ0xkJ2 z!lY(JR$9#jRLo~&(b%>=QS8;EopU)yemUPL{)8Q|X!q3%OYWw2!TP|d+8IzyC#=5_ zh_|2VMs8v?ubNipTzIhoXk0wpZJ#tT9P9)UK5JFZ1qK9LeggfC4|eyZFblhE*K*jc z6`qM`0|*50rQ1*la^pHS?oaYJk0jXadl6mel%DHv)kpH2!z;PE&eo)`HMYGP4u^Qd z#Kn-x)UGYfHmBi#s*Z2?6vFwuxbk>mcA2OjQ%nL?Gl%+%3kqR5D)p4&jEFle2cBh= zE46X5`F}S)0be5xlPHH?mD4J9fiYRe5eFYECyX8_89UcQJ8Oq^S&F18v!D{{=kc1# zmzvaCQF72hwAved!8L3;)28N|k5Jdw{l|nW!=PT{(1$tol_{60tjK0G!9 ziuIR6J6ulRA}X_I|B)}|kkE23;hm<~Li8NVk}m!_^EvwdIE+wQ@}`JqyG9>Q*X&Zb z{T_7OrZcAa3qU}`5ra94mcQo1W6-2tYu*~^knd#{cPLa0G(lS#oOh!%&e19}M4}bG zY%k_7P<(t2a>aSC_;=3AuA~7ch!U^W`liU>{5m8{QJKQ0)KOa-eUzw5m4?LQqBJ^c z-Y85`1ef$>SX&jg4FxEim~9v_UoYo{OZ)WI)q(C^Sc{$d_-Yy3H<6VIZNc}BUUTds zF!m2blRa6f%$0)D{790GUEl#BSCLw*twn7{6_dt4rNV{B>B~K#)A&&ZHmOwz;KEkE zqBpK7ZsUc*rb?|c^8Ec{J|^qh1E_zSB5vbH;qRxcnQgzcwvcg|T+4b-g;bw2eul5} zVMiWSCvazoyfuLUp5GwnHiL57RHE%^uXVQC(okD#QmL-zGF>R6gsv0{Q9PBUG<2nK zL=w|$iX0^Wz1(`%?YRsXOiM%I+(5`^<@qV(!TsI9IWb$U*f;PA$T6HzhrQORx7|JF z8x^DteR{xJ?Xn*nYjp5M$R6ThSKu-8^6>8TWc|a!gqT28?)>aGnnhD34R{B5e`*kU zARGt!OSqDcXi)}~7X;`dNIvPVWwW{ zUQ&H(Cr^+$@W@6RKR=TCg0v>iD8bb4A^%CM#;_qRL4-L_*?6Iy#ze|vy?cfoFS4-c zfA9&K&KGBTOW@0F*+er}#Kaz+gH>8oH6tzK4~-dcjyi>ojp;VurW$LRvrU-+6n~~X zUCb%?i+&F&<#amyt}jo!7rG2g`r4WNHQ2QDZd6ion+oVM?>8wVXy6>3VP_YnF^sfI z^{5S=3%irB90V_1P~jSxjR2Z)dt`E&Tq;v@R@)1XB~*$U5a~L1^zr-WYi5d`?{s?- z6*^%GFi%D&y#SS8zeQTiFM*knh9QBK+2unH0i9RsoQp;&lyZ1uahx!s!~2@FNw0lgw!lNsXlz>&hCrStV0sC(I> zv(2faCaX&`zgg5k=PCe`0k-+#k0GBP||C=0)0Cqt7f~%!W91Q-NQ|!!U@mx z`@|PW034{q$p-$`uX>o-YEVYo8E99Vek1#kv|eHQ{KbH;&jS?qAt8~!>{(N8IO{t= zxd??z~Hlzk(PvX}ISaZ;!p;#Ez^go#d>C1&;K zRl0#w1CNyBYCN7B^>B6>a~yT?!w+M|d~Ud zUn&g7B!kv`Wi+L)&CWaJu*J}kK~{Ba8=_*oK+m3BwW>4`k*T`{Kpch z%2L!f4js0WHDSY*TR@-vu}`2Gi!l_%oEZ02({$GY|=XL z817yc%&3xLr5or5Ro8T3G;p*QJO(Ko2`XKtBw^aqLHV>^wcFn#Ke*F`z6zJQ0TCq0 zxKP@|aJpIAJOL+SdhGL8EdV_~5fFqX=0;mx@~I-SnOFT-B+(Pu-Ia8)#uIxs8p1j}?5)wPPN!r@LnJ~qpz;bQD$kn@ zU5MpI+!QZA!u3Dnz<*w=`@>1rV|n#q8jpU~^t=xAsgOTavGm#Kgz}A0eY4)-lU-e6 zi)SefPj9A9axcmq2H*!mpT-fG#r8LAP@TI3bU;&NaqHSYY#Rwto2ATU8B>f=5>lI- zP%4~0_ane=B&h&7FV`U=KL3zo(1hzcKV1LIi)bQKo>IICASZfYb`{E}iuS06vmrmq zzBGE~(s~iAYqv37VHqC=jC|#afcH9F(m*)M|D}XsMfC}xYahV82jSY_Tf38AZ+u3a zh@7w6_5e2;t|Vy7y1z?EbC~_AtvqOG2T%to8>8&O4&e?jt@#VCsn@ z7rBRa6YS5)-N4Na`$Lvj#rC#K!nZ!$w0}I4o78LG10--?e0iEmVkVthZUH+#m;Ahw zKeOhR?J&z_q~x3dVNXMGf1_tFTlEhLP#F_i=e0u?@^wRZQ6t2PJLB9_5 zJMb@4`oqv0PtMKZRsoLQ)&uyXBdWZyDwgC*1F+`HltpHtM{EpNjd{X_)s3tkr+hXi>?9Q@O zDJsi5fcpWjj1EAo7GC8D&2iwe%3J!aW((rM3FIY|K@lhf%wO(`0-wrLJ28J{S&$p2 ze?t8(l?;Lm*5scY50yhvVeWuW5mh7k+gY_i^fo!t-_AZ((B-rf^&tkNxOcw}rt;!u zUYHa$$wM*taAO%FD7~P!9FniL1HYf()?d{i?l98I9XZ5^tDaHV4|o?SSP@!r<1Pbj z0!R&#)v_w{UH^kBY@SM~Rq5obwcZs2QXxrLaO}|hb2dO3jm4CN^!qE2qa^01D0O4m z$b&?cH}vOqO;UiDGt-r-2{+S;*4N3$W6wx=B&UrB4t}L^~RTZhkJs=*EDP9 z@8BqEW7|rHAj9KSg@w4zk%G>?4JmGrlOlAj$&f+(iK|aFlnxY^q%dtu)fuiQ7II_y zxOfR~I^L8YomoS{U6D@Y$2G66C}N=7u<2AIo%f?@{t2@9mshnCSD)brMJ7Q_w#%0V z1!Nd6%3-EhFI0(51Fn#rI_q=Blh+?sf|MDNQ0mQy}H)aEvRvTe^5Bs4qu2|rj>0q@8Nqu0zDyG`m|5U z(E5<_4!CM(dgsH3hF!>Mm(`AMZFJz#ppUQ=%hw@kW)mItaRx8{wP$C(LoDV`pEY7^%sTE zShZS%;;Jf!&=UI~i)TIWwx+U(n8v8=FZ+(45z1M_&H0Tvm^ps!DzyD<$~#xpBZ{zT zzjfK2{i0owu3)0uV1!8*PbKc0#@t2+ISReu;hI|8eS;@98jF3Z%39ppv$0$*B4w%F zmKM%u)d#m6cX+yV%zA+9i6%`1c05%+-u>(vfn8$0@xx7lb1<(ux9JC~7!A^JgiBdz zFr818N8n1Uxcw6%`D3L(UzZt>Xvs$Ei@6XN4aeTYp71Xp0J23ML8de0+=?S^tO(ABcA!mP$H$uAcvDvdd2p)S#8a)u~z0EKI4Z4W(~G6)Y1 zBlSOUmazI!4x4bC&AiQb3uyS~qi|OnF@YPk3PY)!2hqpOBpGF4%?!N()$X0UbaY3~ z3m(UenqK6YAP``q#4HP9YC;Z6xmYocnVP5!!pzVp)947F$Ls;rl7;e9t@lG#z9Bc7 z@wicD!nSz?Y6f8-^hjFoVxBecD0bM_*nSQ5n2#2tlFpmJTTtvGsn0eoEE2>)tY_`i zi6-r8B4qt#`j{CMcwQeKQHWnatV!r|q2l?PZ*cMeYpP21Q(yY!b{+Xv*<=b_sTs5^ zN}KN0mrP%@ej#iGx72!SuNE_COHK}8dFiz4bsji>zV8~49<2xE3yVnJqG}yrBN7RI zM%ZuSNUSABW&2Y;mrePqJTLQY*=A24<0B~8ObU*E;3M^jK>h7QjhtrdE-RpsmMi&9IAwA0i z!}tS(e`d;;w;Ky573;4!u44Unc5gnj{W5DqSZRPkD*z7rN%fJHK)QmuLGQt)}IK5ay{w4_`A^5nqXi`C0r8wd;Dx z!UektBT}ovlRX*(o-!*K}iGGV8gPB#1t4{r*|K}qhJKp@Cxb>61akUj=ll3RG z8^df~_CsxRT1vkvI{PQCL=a^`kZC5$sfwNeJGY`xHg#r$V7oIJl;J0?A7$q6&efkQ zgi6bq2THoict6X#l1sy&_ufbLRn@#`Cd-R7`VXYo$jL;R=tH4;p(aN#av@{)i8k#* z%I=puli5+|?Rs)xyLRQchiao6V^&#>Ah@1#N$4SB75Ow>5DbleN(G97buBrcgfcto`K*eXe#{}QT!dj|b z?f<$A%tltR6KA~pO5er&i4q*W(;|K?recQuhpJZ;1QsI!_n6iOo1&TKABG1I>G-f{ z{^ZuegZQH4XvDDvTSf;AcIP@qqYjm*dV@HN_VHIiC?DFfwkO?>`l}(Kd8KDE!t&Ou zpl3o}d2w0+8(yXE{=B;ArcKYe_z*JBRS@4&mCpW;FulVZu&d7L|Ot{ zQM`al3=j{u8LT6lqFA=&>{=ce6)H+!W%jH_Tg!Yc7_!@>D(J=_1O3GD=T;~e}t0TdMzw8A4=MpRwdoT zY-WnKzuoo5)gnlCD#(D94J4 z)qcz8SWS5oG)*{nkgG7Nu`W)NFJ6sh4wocCfbp|=pT0e=1}nsL2Fe?%z10TW)~qrY zAjZ?avH%gpDlq1MJ+@I8@*n=9B~4P*N1MHvn|ubP{EEW3d&g#*MUs*yFDRbQH!Z1n z2G$PhZPY6hAzL80-9i)aEnMM1c(onuB9@AjX6U1V@JdLa`TigXUSj0envU zD$IS?GxGp(mJ13pZP^o_pxm#DK4~-#= zYs>PJaW=3+>NR}0h_~~7(P1+POESH)OfT&et31E07}IBUEDB{LmEOIhW${)ZW=`MAPV*Y)G1b6u=wni`TD{^R|4JxntraD>s+T&r^s#2ua~NN&hG;}A~K+wcp5FXBg45+;Olbl2;3bj zYyC}el0xk|8J7EL4QxR)tQorQVU0AnmJfgQE9eQ&R-rQCxk-1QO=Pm&yv<^=2UBZx zK%UCsT4HYbRPZynz>J_78@%z4ej~|mEHzoeR$KtKNewmV`r0lI<;L$#=o0snN7fEw z8sv%A-_Z9W@h-y~mYk#GrS#4=>K5V0+4}Nj>oWK)W9ueUbpYZghI&?hzHhH`_eIX9 zDK80Mf&CONmYS(Pcff0x_Yt9 z?`gmmsDiqfHW(7}Z_*0IsGS^d`@t&{fYc=TM^}Q~!Qj$X&IZNupa1&j&)=|Fp#Jff z&ly-`pv~}~AwfUi5ujivwU?-s;r_lGbZr17sM}>bfAPRym;Je6OZYUf!wXaUJAI;`6G< zpjKzv;P3o%D*CU5-+}=dcY)iy(ACS)oVXdk%VBaY`JMLYt7tyoQ>5#Ye7@cDU4*}` z2Hoe2s;`+(4E<9?gO@RL#Q0;552YW9ZuUoPWO0ptc^I!tRc{W`%rm#d8V zx3eN9{SZ+1W2u>{1L`LppV1s$M?(wI-_!h#2l8%fOSB&pA*3&lB$z5RcMN?X=K0s^?sm(KpV*AZk+R2M*pp7&SoUuTXO zNZ3j5xl}f&ztsquNIR(c@xgj${=q-*2Rd_*ft3IMW2(jvw(}i&Hzg!K9hmbIx}{@wdnr6bIEDoMul zY>7O(LYEw3kpVMoJ+5g7k7I@0B46c1M*Xf5$+xzYTyWwz)+Hiqu3nhs?$ zuWoF;Ax-j~o1R=RIwL7)@Wie%g%;)gCSthfb=yT2y~KvkSx_?4S88u*`f}0BoUtc0 zlslq~^skLghz&BELp)3~C)sM9t%w%hamozxODzLwK5d0>j7G)}_n*x4mXOf9>7zBs zVveV7!zO>M$e1cuf@+*C#O$#E%Fknvygc$U57KlS(xo_9N!=Pp5L>plba6U-TW-?? z1~|yu&5cI!LD8xdk6O0ip~o}j>E&!T8E7dcy08PF3VyCZ6-STu66qs z;HJdiymAd|i`JtQ`R#-bd69l+RcxqVl&EW+yuY+mY+h$OMI!y{$uBhB+-AP#%R%QX zrB}eLS=ngk);_p#WC9)vIffL{Q$_CH#LFy;S6jtfMsuf1^c4E2b!rIJ$z(rQ+r|rR zHZ|MSZw zdDUo${6FoT_cvVs_wUmr5lOtG1`$cL=%PkX(+APbFi4ca=ruY?L=r6whGBT?88vz* zk+(36&S)_tdM|^CVYnxs`~AGhKXBK*Yh7#k?aVo^vtRqQpL;)E<*ZgIwV`G>+X~kk zQz{ae9VC%)Jm@3z5NpS0s)4fXXnBU?u^)<~0|IJ!cKL#gMdj)p3#n!tw)i7vbD45q zukKK6>=Lf*BLr_Xo*?KF=s*`Yj;n`zTjt8`caxPRO5Y}W=ltw0Z`z7$~v)~8o@eAmE+o7b2;`xbV^|~@7a5Kklg5d7f4{N z@}=4K*%jN9fvNfTy@u0Q1ZiYX;Ux~}b^QygB?)%Es1Ae_n zOALaxfzao42~ST1p^f^_m}UZnpf@%z8(M8796xQVk(H<}B!Y}iSbjGnaz_Y+IvTQ- zwT<_+(C)mzqYmuMJnqM48quP@b%m9=-BxF0`268*7fCzrRvZL24e``47$@Ogj@0F{ z{`rf)Qui9e2QmVKy&AgeI_H-U6MW_B9UKUonI|3{ z@&JDc_&J>7D|^CuFW#~QqSz+w>3^U7TVq{APYtj_d#PEB!VbbxO^tD|s-4C%dfwDP zzOnXXIpsWVd3)G={mcvgbVE^Vbop!2wA_iC1~CKDYDGKW&fU0N9Z!%%$rddG@oP}P z^NnZ9<}1Ny@brp~^YnN3A!|>fCR+b_%nkP{ooe0tu;Kx9+{~teDlM;Xr(TH0T=#Ot%EqQuoVaO)grMFdrpjfWEW8k+xIBpN-NcVndY7+} ziB}x(=FUp>(dGV2yVn=XUF(PxPc{oD6Jh_Dq@4~uaCF*zVM=1R#;jrLar_JkApz!Z zm<%_faY!t_^|i+yMrG-ZU`m-D;l@6Bu%AG+I()Y$DrtZcLu5N>|3qoZ~TX%{Wr+w0op5f z<-;;9vmk%Ev@@K_%p2-+Q}Hfs7v=FAVKkrnK}}`-@bbP~Rt~oGL_8SIzW>{$n^&t1PQ^lTWL|DE79Na3YH5)4*a3*?e@<7HN{@;HR6N zW%G_BiP|5>fMXLFinVRk=%gkF4^+Fv#R@PdsocQ54kunb9;oN~bpY-|5fmqBD{FQv zdzs4i95_$&FG^UszgMZXEw&J@lq!r*$(N2j8d9=&pL9w!PtKvwr?Wu};;lWJFIL(* zt0ysxiHsu|F)gF(HM`zUZton)DmtF<>nZQkco`3WT5jFuOQRmLs-BR{=(jlzwzy6eB)vNXUBu7!HG7E@|<%#e^4T*c8Zl(RPOm$mJ&o_IS`uDyO{q1Vy{fqCpySg;Io(VMf}`azF| zSI#U8?7bZxd)6O4{fAAAey51LAg-N`Sbe`Cr`v%JQV6rYK0LdGx?0hrR_g#RlIB-Q z?~V93rr8Vq9MADQvMzc!R)PBiXIqJk@kQrg?;=NzbDb$g;C`eX^?W!3dxDm5iJrxI ziG~Ln>1OF57csX1)>;F^>y?qzCI8%=mxlY^a_b>EJLc3SF;bkc1R91a?{up&iD=V% zA_Ts=9${13m2&C5|4`ch!GA+QLR!3*7H_y#p1Yc3*=^R1?ZnC1Rik`La*Lr$E1K?K zL8k?6>Fn$<@jd6{_b$Cl`pjRH(@%OV=6P*t6ulmQK1{Qn-uBsQx>QNRBwnyIKj2{) z3a>~J3no>0+DCX<1)c2EFTnFyE~m0oSD!5(`5Hd+V=uU4s#P5KbD%9Q*fZK}>(MKt z{?6=yo*;L0#S&FSdy{~F2R$*b!=EM>eRvbAbo+4(L3SCIJW*9(NU|`s5E9TiAD64((nVbkOW+(dVZS{FYOEENAGtPl6%%+hL1FBp4@N+IF2lT@_En{>@hq*S@Q>nENl zUOO&3HeLf-!&}FxrUZi}gms3M zCOdw8gok+v8Ed^M0t+IHawrNF3U4KJzWR)c81-kP6RDsdY~o&hg(~F5iIU& ztzpcJ4+*8}es)Z46n{FUwS{ZG*oK0gIBdv$0v9VbS!nE>Ee(Bo+szXJ=r1*(9545r z_1x>i)!Rw70UtfYs*M*r_5TRJc8AAJjcYc_PsnDt?{Tczl3Dq)BlA6LEXbIb$Bn=9 z{E3#>1JqZO^Y3|K9d%2lv5%awR3@~>@aulax7BP6e8MEHJ>j3rwSS!u(UOZFiuwc; z7<>H)rnOC;qWbs#APrEUnC94aojs#`2d?}j)4dk^{l53!_x55PSHryqs#FEt$)Kd% z%Q3!M6IB(>>RYqzd|4>-JA2iwZ&${azNi0@)$#z9Xx~k!$Mz?Eq7Pd|VuaR*A$3GR zwrI$rIq?Qd@5iW_?nJ1@%J)0t)lQyk$fc;3R+~8WzR6;PJ$)pKskfo)+j_OZ^y8Is;y_XuFNpo`^)pP^^t4I4CN|w&Ss2@oHbWZyr?nt6SQcnq2#5f zHkog>we;ct)fb7`MkZ)v-BDR^_HgRtldJ5XcDczmD%nBuC0PhPix?7k8`&UA%zKiT z<{i#5Sj$!joz|aEx8ulG@hBYF!(LIgMr?l>cA_KB}a3=yhULe zsCvGx+8(2mQ)d>10lKi3&DZ=4@=APJoo9sH;AXn`9M4(asg2m4&ZrG;@6ACiZqziI zuJn~P^vM(knrfZOqcJ(U{xJU_qPtiqUBX_#W-ArdQL{J|#Frpf;|9ZNBf8#z=vA|hbcZy&rf^DW$8yEni!PT7ola%G)Y*NPA;a%AefI{bMxW2{ST zX7u}?1q3i(C%AYysQYJscv?}#79Rl8QO@BUJ zx>#zxy_G7Sx~C|<uVj_x zOlVw!(*u~;$mqvU=uFaOAjUY~C* z4E_vHX(gq=tgz(y@I=4dXowVtM(bV6g)1Zff;j*&aRZVfy!iGq<{P1gFW7IrOFa@4 z7U-NL7%3aJS-Pih{W)`y2isoy38`FHt|FFYKK&dx9r&)-wo485FNc*eo-&`josav= zXpydngsQ4$!Y8r*HV^g_A*dLbfAvt7uF0c|1wFfLyie5f+xNN<$I-<)xN)Yo#-M%7 z?uWBAfzRJ+)X}%lj}v45>Fte%%)Eojz^sJGZ1>O>JfpwX)UVzK%#Y$f22L7~eiA{( zHm=G8qG9*Ux(>fPFjXpc$-?;9yZi&b(dzNI1T8uw(zG+O3l(X`d);J#Gu1n1ET^Jx zXn9;ci9|x$05l|zf3|2o0ZMDRu1o2X=vdbB#?p2eBkQ}1IV&$QB1W>pt@Ut_#k|FR z+LfqQSJFLm?d4jK_>B}vx-edp5D4nEr|AS(+lv9{m z3llToiE4;U?pZ%Xn49_eBICsO{BHaw>=z1za2o6CHka+Lfo~Bv6ynyv*Qb@ZzetOt z%dCqV@ZC2?agYUHnk1jWU9S7@N=~@&In~bxh9K&O&p%!zvnD~IMHwp%a}Rm-txnT_ zWUrq)?TfMZzgBK1b)w=7y#?S&s?y=|PhArZX`EIl3!X5Kx%b92(6$u}tCsbX0)gq( z`ICV9AMN-P|;g=M7X@QS*u|EcXZ5VM)MPwYlDrDi5|(ne6H|}DWJrh$&?f-{3C~#Ff4(j!{@}lo zQ=hDNR~NIq#bnC>Uu9GPHl!A5D+nU;Ld^7v^F1a99S854;A8^bTjGD&UR!LgN(n2_ z>hr?gJ!w*{TVBDK3tDZ!UCRM^g9^(l1J!~q4_StGW%r0L z;n}I9_%c_L6h_|dBgm|FYI`_b#)=gCIB$2Z+s9#lCh>d_#GCydwZ@obn$>>dQ~F9%;F1XnK)qHc=Ei9)JFyWe)2xXu zePum6=k=~c{YRSmJ$R~kn?&;yF7lwpRnoAW1wZp$NA`4va=c8;Ng;lD$91V>`tOSS zcB>NlY|RiU(75_*a~lTO;_uHvs2QOI(q~``bYH z*zK3^d6a?FS{c6iFIB~pSgWeW7$nI~Oz$0J4bg5{^^;+8*M>Rx5P6rnAI^*(sagqS3=Rh4O zF?%O|RBOGAMdfs;$6S(}S`Erag)BG|gTTLeXSzDF zMEQbV%#vlkFG&#lJbFmbkZ`HJ4q0-W+kHODXUXWwQXI_+==q1EWS^1k@Dml{nf5>Si72M-)@C)ht81hp3Fi>G-5!8YJen6; zo_$j`L>Kj8(T4Ry;PW$Op>@t+S*|PCmbHufb}!3639cO1OYNtCjP~*atwo^;rd05b zls;2Q{@4RWJJ*LJmY^OtmG%JE4`I6;U%(&Fj~|aX7QWANFc!>mtA7fqt0jj(+P|30 zT>oD7nC$=342cD>v5s+cD))9f!h*}RqP=N?ItDnwgA*3IV9WY1v#5ClWUt1auObB8 zEK4E;REJLTr;>3d zd*k1n3b;0a3rKOZy0cH_(6^?bKvlZF%Px7s|4*If0C?8kM{)a;i1y+OV;~gN2dZ|R zs9qj?Rt365aS4R_qwD{VHuZPvth$=<$7_Xz@KOQJV_mIl|A)0w0BcS9#qa)MEndJ{ z@}o`$tS7B?@Vg+WzL}$N-2K4RlsaH79dEXT6KTt-)6KuFRitW9dqOEMzR&}#g_uyu z`NLW+fVF~_b;n*E>-DKKX@IK!KhxCqH7rE+>0(X+z9ZwSYM}t!7<0F;ukYITFwHyH zOiFi^fZ0#W5zX?Hpu0xdf5*#anT$-cG+(%(Ptk;NU zR}zLL-sNeHV0yQ3^F$sq6%_iBKDVYBWnb`PZdEQDRM73dJ>s^kGTAV!BWp1t<|IA4 z#6UB+9)Q4^R*8)gJCVYC#?XM!hOKsJ9H;6WMfz2Qv+vU&GCa}DRr~Co%|bmEf3M)G zZAoC$4e=xI{qF;h)9Ok1!u<0+%D;!wfAZaS%g_fP_zgwxIQk^ad`M4Ix22JDIwZ96 zUdn7&Mpib3owl=hq!H7YN<7iA8x0TUuIO6d9^$po`jnQYAe+LNO}X!sTWD7OkUv3A zfCkRkpo-)(G;0c2R?hpO8pDpDT%diY?H^#%dl6i1Uw$AKSS5dS$h*~SPLi81J*Gw> z5B@qApya+g{GF=IdWf|^Ph6HW6a3nQ=4$Ri38V)p zn3EM@f0Qf|c4-drtXnr04i|CT&dgbOw&NmQ zfA99Y^CcKQVpn~@xX)^pFvegP_v9!@&qJj9go{B=TU>y#N(A2f0n@Bmt&QUHDNgNQ z-P$#Bt#UFhj7;7x_&$Pxj@+~xqL{;v=1Y`#O9CY1bst46F?M1j?{Szu#OL@Z+KL^v z2J0-LcA48hN~a+mN42kLFL7*9F2uwKy?PVgWu7GNf7Q1R=Ce3KwB>bOqI9cn&C)gl zUyyG>52#*=B(+hpmGU~ziZlxy6@$|^4D5$$ngYhw`7kDPxEF0J-ocx5#I5cPk)prF zw_lsdxSQx-^{NnP)WlUe;e|R;QzB+YkFFTFyjbx~Zu~X*B*qzjraq%Y-bsv=Nyl@g ze4VG0J&?>R)?5dL%UI?b@(8|CiAg|eabR?t8ftA`Qg>H0h+4j_a2Yk=x$5_oeDht2 zdCfoPI*bXVctOc8zAc%n*Eh$?jZP(Rn6#{CH65ut2mGWG0KbivJ(Qn8zs}n@vH0=v znY=p}Xboj^42P!RG1R>kp6j0Pb}$bS(V6ypu7lNhn0s@bA~}^YX;D(kVOTWF#?6`> zzN1_%e{0n_u>BH_H7Pb_ZpT40dc}>8yfgZ^8GU^nAEM>s34CJW-PE$jox`?)w|_qOWDw7!m54Yvw&a zZDg4{Z6*72Xs4`7uUyY}`3Ac}q0gQcz8&qM+y2dqmQ;F1yl6hGi#7R{uyEHw2B+%G z8Z+U860D0xM$Rb(C2Fw$NOiTs44?0t$<_^S0I`}60aLEsMHZ%YDG%!1ik9%TC^y~l zEKcW)*>q*iSv-skp7V8|uXtH-wWvGcjO}aPSLW=Sp!|=q>`9~ZX12vcbeQ#TLZ&$s zrl>1y`9q{^{MAr`K_+#_NWn9(AfIJ@_U&ze4iXbP-y_qdIPrR_;xTVxg=(ke^_yFTxR}PDZTqt@0XfI z^s+Q$Uqo^zV0E)bS9AVJx!aMZONwss*UX0t$n}Q1?2xvdyfks#xi8hEH4#}>^0w(K z>s}fi6XcTJ93x4Q`$8Al8iS|{YB`@DP+d_{XmZ0Gv5C$Ot|>s(dTMTSZ9|(lTw$QC8T)0*T@~Ew6y9@ zWJHdo@K}<+jb5$J2p*&juB;|HL%_r~SG&JGhP}|}&!7?)TNTgkKnbA&T)n<`-?h{` zJZginmCPIs6VxwOrt7{TqxI}Z8=S%$Ir!1j zY=Z!gYju|UOfr--)vbrF4XX`bWnRqnOZLJ1JMC^olnz~wWWTrw9N)`G9p^rK3NKuQ8-!AI=1G2Ay468-Itmyb_nY_I4MmW&DqpeZ_uWRB~<} z=T(SGA9b-7SBMEtBtW_q;KPZeRW3Rf()?~`w5qUkW^akcg; z+JA%%RiT`oZe~xP8_c(k7ejcI|-)%M`VR+i+9W=Ouv~ z;J#oE)^@mMqG%{6Gvegz8Ov!4ZMeKGzUNZlq-NeGw_rHVKw5~bJ*Fn!s;jEp!pJV8 zX|m#j_iS!jM|^sHQ|E=C_j(YEqH`P$QZopeH-$ISSBW!52sg|9)ZClw?ZZ3uz@Mrr z`eKY*RFfRvxII)K?QbpOy8BLBR4(5z(w_v131g!Y2)6w$XG>i&bomWLJL%Tl%A7~Z zdRqf|F5!(JRi=8ygH%Z^j}YG@(An?iKAzRx(5BtNyU>JPXp1>VokuNLT1#AM?BPT* zcZ7QMjUPuCW2yC3dzFst_qMM5wR&#O@Qm;O+uHJQqyXN9&GAwjq^THw!vA#If*w3q_Y-?d?avK zkz~M%HShg#S9eq-YIsUNc)t;)eSBMXDu88^TCN9IsFD0Q8&NvBuC|{3Uk3JWpb&UQYIoEzATo z!l9&{ViRT7C9eL^vvg^0XPHaBHM>HmLLiepIED1pPkyItN44_2GI~5sWM1+kyvQkM zIokeb^f%+l=TtD%{X5T+?**5tpV@3kn8{N6a`%7d4rbSVnS{@k8ipVaVd-#WJ|laapL zZd263Sm-lUwW=mNHBn_{ypXe5g;?5G3taWK6D=IQ{&@0L1xMjJ{TtzC%k|+N@=!ri zY+LmVyEt8;Jw=iQr+>s|C*<^(-^~-ufxxfWZ?P;OGiSL_{z_|I@gNmq$P;~9@~4)?3<2`iHg_n`=vTch1%ipPI`4O ze)8pOYNN(`G_bJjri4zk&4l@5mq3y3?i(y*P+;Clo4xpKgGwd6O<%)73sYu2|hMMTzQPSiN=EjP7l3zJ0zsdue~> zvWnAW!uCbY1+Q2~5lC%E?aKEzd35hbMS$JQ zE?0H672-}gu_MB9gRzX%N(MJ3OQ_mT=&`` zrNbl3fbb3M;zZ;f3YG5fV!_Xt2C8%huT{rb1n89|^W~1URVQ5=rBLT~jLmQTONnkx zv7er#g1u^vG#wZ8>ArzS8DPD_nhFMHbxeuHB8j>=4tq6y9F0d29G5-Bo;c*T z$Fv3TR__M2`fjfws2_PQna=zB$!g-Rbc&T zYf6F(4B!1--gf>|+afsHyWI89L%0L*X`y2}0~VMnu#X~}BO5=uwn+)4>Xy#Db!n`7 zCAp*3!(vx<&f)#u$82*cjWv}#8Si~bwDlD8B!uQpvy5jvHB(cZ#%y;5!FOQMk8Ey6 zh0q9$N=~bGTQ-6-xD@zuEX9C{sdBVp`rF&Q821$WPSwElimS~>E1X6UKj`{OLX2wr za(WOCch6mZHfb07{V1s`42p`rL3$)m55=^r-ogj<`Yx8y>(}2socQ>EdWnam8H$+C zqPxK3A9<4?(a`O4eBi>55YxH^L)r+(p(~B~d&&HfE;6v4jtdSxsf0$ft6tJUB1D&5xs4wxQ zr9y_L&`Iu{nrGF*6XbZ?_5)APpTDF`t7-)AjDr4thJ@HYfqf4-{jxw=nlPXG#`N|kKS zv25Fa2L0uOCyU&Bc8no;;rKph|AnSr|0VD2CKP}0Va>Bl&crbhS*qcyI1MJD=R#4v zq|otc1z}nKwMk36qb2G+t?~0s8#P|Ve&2U-m_B1TZ}Ty{mFND;hEDg1nClcqr4Ca@ z4jPmdcPMk9sEC&#bsUWHlYU@&y~a|DDV2TMNiHVkDDaBxnu45kZFa6OS;r_(yx64r zp~OHo4=rDk(xkmh239w*$efpIYpycRjf*(|y=!}$_sLShFs0tMaY64i3@I~!GDnG? zqVc`_zc&^BXGkdiUOwo1VL<*^N><}=3EhwxFP@|cAGrd|Pj0PE~@&e1&{O#thZ z1XyRu&h?#Rasgax0Zhs5m6_Tz{J}c40M-dtPzgMCDquPn39!zl6(iQLldRJmSdK!3 z#OPZ5(XJc-%Zk(|sGL|}6cREF%$YUdtp=I@%jW)nQ~$qJ|1a>>&qt>Tnnl(JJt3;6 PfIoF*?I*>LEMNXFD!$XA literal 0 HcmV?d00001 diff --git a/strands-agentcore-smithy/example-pattern.json b/strands-agentcore-smithy/example-pattern.json new file mode 100644 index 0000000000..714e25a40c --- /dev/null +++ b/strands-agentcore-smithy/example-pattern.json @@ -0,0 +1,86 @@ +{ + "title": "Strands Agent on Lambda with AgentCore Smithy Bedrock Target", + "description": "Strands SDK agent on Lambda calls Bedrock Runtime via AgentCore Gateway using a Smithy model target, with Cognito JWT auth and IAM role signing.", + "language": "Python", + "level": "300", + "framework": "SAM", + "introBox": { + "headline": "How it works", + "text": [ + "The user authenticates with Amazon Cognito and receives a JWT token.", + "The JWT is passed to an Agent Lambda which uses the Strands Agents SDK to create an AI agent backed by Amazon Bedrock (Claude Sonnet 4.6 cross-region inference profile).", + "The Strands Agent connects to an AgentCore Gateway MCP endpoint, dynamically discovering available Bedrock Runtime operations via the MCP tools/list protocol.", + "The AgentCore Gateway validates the JWT token using a CUSTOM_JWT authorizer backed by Cognito, then routes MCP tool calls to the Smithy model target.", + "The gateway target uses the official Bedrock Runtime Smithy model (loaded from S3) to expose Bedrock Runtime operations as MCP tools.", + "The gateway uses its IAM execution role (GATEWAY_IAM_ROLE) to sign requests to Bedrock Runtime — no API keys or Secrets Manager secrets are required.", + "The outer agent (Claude Sonnet 4.6) calls the Converse tool, which invokes Claude Haiku 4.5 as the inner model to generate the response.", + "The Strands SDK handles the full agentic loop: tool discovery, Claude tool selection, MCP tool execution, and response formatting — all in a single agent() call." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/strands-agentcore-smithy", + "templateURL": "serverless-patterns/strands-agentcore-smithy", + "projectFolder": "strands-agentcore-smithy", + "templateFile": "infrastructure/template.yaml" + } + }, + "resources": { + "bullets": [ + { + "text": "Strands Agents SDK", + "link": "https://github.com/strands-agents/sdk-python" + }, + { + "text": "Amazon Bedrock AgentCore Gateway", + "link": "https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/gateway.html" + }, + { + "text": "AgentCore Gateway Smithy Model Target", + "link": "https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/gateway-building-smithy-targets.html" + }, + { + "text": "Model Context Protocol (MCP)", + "link": "https://modelcontextprotocol.io/" + }, + { + "text": "Amazon Cognito JWT Authentication", + "link": "https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-verifying-a-jwt.html" + }, + { + "text": "Amazon Bedrock Converse API", + "link": "https://docs.aws.amazon.com/bedrock/latest/userguide/conversation-inference.html" + }, + { + "text": "Amazon Bedrock Cross-Region Inference", + "link": "https://docs.aws.amazon.com/bedrock/latest/userguide/inference-profiles-support.html" + } + ] + }, + "deploy": { + "text": [ + "bash scripts/deploy.sh" + ] + }, + "testing": { + "text": [ + "./scripts/test.sh", + "./scripts/test.sh 'Ask Haiku to write a short poem about the Beatles'" + ] + }, + "cleanup": { + "text": [ + "aws cloudformation delete-stack --stack-name agentcore-smithy-bedrock --region us-east-1", + "aws s3 rb s3://agentcore-smithy-bedrock-smithy-models --force --region us-east-1" + ] + }, + "authors": [ + { + "name": "Mike Hume", + "image": "https://serverlessland.com/assets/images/contributors/mike-hume.jpg", + "bio": "AWS Senior Solutions Architect & UKPS Serverless Lead.", + "linkedin": "michael-hume-4663bb64", + "twitter": "" + } + ] +} diff --git a/strands-agentcore-smithy/infrastructure/bedrock-runtime-2023-09-30.json b/strands-agentcore-smithy/infrastructure/bedrock-runtime-2023-09-30.json new file mode 100644 index 0000000000..1ca7dafc55 --- /dev/null +++ b/strands-agentcore-smithy/infrastructure/bedrock-runtime-2023-09-30.json @@ -0,0 +1,7561 @@ +{ + "smithy": "2.0", + "shapes": { + "com.amazonaws.bedrockruntime#AccessDeniedException": { + "type": "structure", + "members": { + "message": { + "target": "com.amazonaws.bedrockruntime#NonBlankString" + } + }, + "traits": { + "smithy.api#documentation": "

The request is denied because you do not have sufficient permissions to perform the requested action. For troubleshooting this error, see AccessDeniedException in the Amazon Bedrock User Guide

", + "smithy.api#error": "client", + "smithy.api#httpError": 403 + } + }, + "com.amazonaws.bedrockruntime#AccountId": { + "type": "string", + "traits": { + "smithy.api#pattern": "^[0-9]{12}$" + } + }, + "com.amazonaws.bedrockruntime#AdditionalModelResponseFieldPaths": { + "type": "list", + "member": { + "target": "smithy.api#String", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 256 + } + } + }, + "traits": { + "smithy.api#length": { + "max": 10 + } + } + }, + "com.amazonaws.bedrockruntime#AmazonBedrockFrontendService": { + "type": "service", + "version": "2023-09-30", + "resources": [ + { + "target": "com.amazonaws.bedrockruntime#AsyncInvokeResource" + }, + { + "target": "com.amazonaws.bedrockruntime#GuardrailResource" + }, + { + "target": "com.amazonaws.bedrockruntime#InferenceResource" + }, + { + "target": "com.amazonaws.bedrockruntime#TokenizerResource" + } + ], + "traits": { + "aws.api#service": { + "sdkId": "Bedrock Runtime", + "endpointPrefix": "bedrock-runtime", + "cloudTrailEventSource": "bedrock.amazonaws.com" + }, + "aws.auth#sigv4": { + "name": "bedrock" + }, + "aws.protocols#restJson1": { + "http": [ + "h2", + "http/1.1" + ], + "eventStreamHttp": [ + "h2", + "http/1.1" + ] + }, + "smithy.api#auth": [ + "aws.auth#sigv4", + "smithy.api#httpBearerAuth" + ], + "smithy.api#documentation": "

Describes the API operations for running inference using Amazon Bedrock models.

", + "smithy.api#httpBearerAuth": {}, + "smithy.api#title": "Amazon Bedrock Runtime", + "smithy.rules#endpointRuleSet": { + "version": "1.0", + "parameters": { + "Region": { + "builtIn": "AWS::Region", + "required": false, + "documentation": "The AWS region used to dispatch the request.", + "type": "string" + }, + "UseDualStack": { + "builtIn": "AWS::UseDualStack", + "required": true, + "default": false, + "documentation": "When true, use the dual-stack endpoint. If the configured endpoint does not support dual-stack, dispatching the request MAY return an error.", + "type": "boolean" + }, + "UseFIPS": { + "builtIn": "AWS::UseFIPS", + "required": true, + "default": false, + "documentation": "When true, send this request to the FIPS-compliant regional endpoint. If the configured endpoint does not have a FIPS compliant endpoint, dispatching the request will return an error.", + "type": "boolean" + }, + "Endpoint": { + "builtIn": "SDK::Endpoint", + "required": false, + "documentation": "Override the endpoint used to send this request", + "type": "string" + } + }, + "rules": [ + { + "conditions": [ + { + "fn": "isSet", + "argv": [ + { + "ref": "Endpoint" + } + ] + } + ], + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseFIPS" + }, + true + ] + } + ], + "error": "Invalid Configuration: FIPS and custom endpoint are not supported", + "type": "error" + }, + { + "conditions": [], + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" + }, + true + ] + } + ], + "error": "Invalid Configuration: Dualstack and custom endpoint are not supported", + "type": "error" + }, + { + "conditions": [], + "endpoint": { + "url": { + "ref": "Endpoint" + }, + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ], + "type": "tree" + } + ], + "type": "tree" + }, + { + "conditions": [], + "rules": [ + { + "conditions": [ + { + "fn": "isSet", + "argv": [ + { + "ref": "Region" + } + ] + } + ], + "rules": [ + { + "conditions": [ + { + "fn": "aws.partition", + "argv": [ + { + "ref": "Region" + } + ], + "assign": "PartitionResult" + } + ], + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseFIPS" + }, + true + ] + }, + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" + }, + true + ] + } + ], + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsFIPS" + ] + } + ] + }, + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsDualStack" + ] + } + ] + } + ], + "rules": [ + { + "conditions": [], + "rules": [ + { + "conditions": [], + "endpoint": { + "url": "https://bedrock-runtime-fips.{Region}.{PartitionResult#dualStackDnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ], + "type": "tree" + } + ], + "type": "tree" + }, + { + "conditions": [], + "error": "FIPS and DualStack are enabled, but this partition does not support one or both", + "type": "error" + } + ], + "type": "tree" + }, + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseFIPS" + }, + true + ] + } + ], + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsFIPS" + ] + }, + true + ] + } + ], + "rules": [ + { + "conditions": [], + "rules": [ + { + "conditions": [], + "endpoint": { + "url": "https://bedrock-runtime-fips.{Region}.{PartitionResult#dnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ], + "type": "tree" + } + ], + "type": "tree" + }, + { + "conditions": [], + "error": "FIPS is enabled but this partition does not support FIPS", + "type": "error" + } + ], + "type": "tree" + }, + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" + }, + true + ] + } + ], + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsDualStack" + ] + } + ] + } + ], + "rules": [ + { + "conditions": [], + "rules": [ + { + "conditions": [], + "endpoint": { + "url": "https://bedrock-runtime.{Region}.{PartitionResult#dualStackDnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ], + "type": "tree" + } + ], + "type": "tree" + }, + { + "conditions": [], + "error": "DualStack is enabled but this partition does not support DualStack", + "type": "error" + } + ], + "type": "tree" + }, + { + "conditions": [], + "rules": [ + { + "conditions": [], + "endpoint": { + "url": "https://bedrock-runtime.{Region}.{PartitionResult#dnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ], + "type": "tree" + } + ], + "type": "tree" + } + ], + "type": "tree" + }, + { + "conditions": [], + "error": "Invalid Configuration: Missing Region", + "type": "error" + } + ], + "type": "tree" + } + ] + }, + "smithy.rules#endpointTests": { + "testCases": [ + { + "documentation": "For region us-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://bedrock-runtime-fips.us-east-1.api.aws" + } + }, + "params": { + "Region": "us-east-1", + "UseFIPS": true, + "UseDualStack": true + } + }, + { + "documentation": "For region us-east-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://bedrock-runtime-fips.us-east-1.amazonaws.com" + } + }, + "params": { + "Region": "us-east-1", + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://bedrock-runtime.us-east-1.api.aws" + } + }, + "params": { + "Region": "us-east-1", + "UseFIPS": false, + "UseDualStack": true + } + }, + { + "documentation": "For region us-east-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://bedrock-runtime.us-east-1.amazonaws.com" + } + }, + "params": { + "Region": "us-east-1", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region cn-north-1 with FIPS enabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://bedrock-runtime-fips.cn-north-1.api.amazonwebservices.com.cn" + } + }, + "params": { + "Region": "cn-north-1", + "UseFIPS": true, + "UseDualStack": true + } + }, + { + "documentation": "For region cn-north-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://bedrock-runtime-fips.cn-north-1.amazonaws.com.cn" + } + }, + "params": { + "Region": "cn-north-1", + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region cn-north-1 with FIPS disabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://bedrock-runtime.cn-north-1.api.amazonwebservices.com.cn" + } + }, + "params": { + "Region": "cn-north-1", + "UseFIPS": false, + "UseDualStack": true + } + }, + { + "documentation": "For region cn-north-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://bedrock-runtime.cn-north-1.amazonaws.com.cn" + } + }, + "params": { + "Region": "cn-north-1", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://bedrock-runtime-fips.us-gov-east-1.api.aws" + } + }, + "params": { + "Region": "us-gov-east-1", + "UseFIPS": true, + "UseDualStack": true + } + }, + { + "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://bedrock-runtime-fips.us-gov-east-1.amazonaws.com" + } + }, + "params": { + "Region": "us-gov-east-1", + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://bedrock-runtime.us-gov-east-1.api.aws" + } + }, + "params": { + "Region": "us-gov-east-1", + "UseFIPS": false, + "UseDualStack": true + } + }, + { + "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://bedrock-runtime.us-gov-east-1.amazonaws.com" + } + }, + "params": { + "Region": "us-gov-east-1", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://bedrock-runtime-fips.us-iso-east-1.c2s.ic.gov" + } + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://bedrock-runtime.us-iso-east-1.c2s.ic.gov" + } + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://bedrock-runtime-fips.us-isob-east-1.sc2s.sgov.gov" + } + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://bedrock-runtime.us-isob-east-1.sc2s.sgov.gov" + } + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For custom endpoint with region set and fips disabled and dualstack disabled", + "expect": { + "endpoint": { + "url": "https://example.com" + } + }, + "params": { + "Region": "us-east-1", + "UseFIPS": false, + "UseDualStack": false, + "Endpoint": "https://example.com" + } + }, + { + "documentation": "For custom endpoint with region not set and fips disabled and dualstack disabled", + "expect": { + "endpoint": { + "url": "https://example.com" + } + }, + "params": { + "UseFIPS": false, + "UseDualStack": false, + "Endpoint": "https://example.com" + } + }, + { + "documentation": "For custom endpoint with fips enabled and dualstack disabled", + "expect": { + "error": "Invalid Configuration: FIPS and custom endpoint are not supported" + }, + "params": { + "Region": "us-east-1", + "UseFIPS": true, + "UseDualStack": false, + "Endpoint": "https://example.com" + } + }, + { + "documentation": "For custom endpoint with fips disabled and dualstack enabled", + "expect": { + "error": "Invalid Configuration: Dualstack and custom endpoint are not supported" + }, + "params": { + "Region": "us-east-1", + "UseFIPS": false, + "UseDualStack": true, + "Endpoint": "https://example.com" + } + }, + { + "documentation": "Missing region", + "expect": { + "error": "Invalid Configuration: Missing Region" + } + } + ], + "version": "1.0" + } + } + }, + "com.amazonaws.bedrockruntime#AnyToolChoice": { + "type": "structure", + "members": {}, + "traits": { + "smithy.api#documentation": "

The model must request at least one tool (no text is generated). For example, {\"any\" : {}}. For more information, see Call a tool with the Converse API in the Amazon Bedrock User Guide.

" + } + }, + "com.amazonaws.bedrockruntime#AppliedGuardrailDetails": { + "type": "structure", + "members": { + "guardrailId": { + "target": "com.amazonaws.bedrockruntime#GuardrailId", + "traits": { + "smithy.api#documentation": "

The unique ID of the guardrail that was applied.

" + } + }, + "guardrailVersion": { + "target": "com.amazonaws.bedrockruntime#GuardrailVersion", + "traits": { + "smithy.api#documentation": "

The version of the guardrail that was applied.

" + } + }, + "guardrailArn": { + "target": "com.amazonaws.bedrockruntime#GuardrailArn", + "traits": { + "smithy.api#documentation": "

The ARN of the guardrail that was applied.

" + } + }, + "guardrailOrigin": { + "target": "com.amazonaws.bedrockruntime#GuardrailOriginList", + "traits": { + "smithy.api#documentation": "

The origin of how the guardrail was applied. This can be either requested at the API level or enforced at the account or organization level as a default guardrail.

" + } + }, + "guardrailOwnership": { + "target": "com.amazonaws.bedrockruntime#GuardrailOwnership", + "traits": { + "smithy.api#documentation": "

The ownership type of the guardrail, indicating whether it is owned by the requesting account or is a cross-account guardrail shared from another AWS account.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Details about the specific guardrail that was applied during this assessment, including its identifier, version, ARN, origin, and ownership information.

" + } + }, + "com.amazonaws.bedrockruntime#ApplyGuardrail": { + "type": "operation", + "input": { + "target": "com.amazonaws.bedrockruntime#ApplyGuardrailRequest" + }, + "output": { + "target": "com.amazonaws.bedrockruntime#ApplyGuardrailResponse" + }, + "errors": [ + { + "target": "com.amazonaws.bedrockruntime#AccessDeniedException" + }, + { + "target": "com.amazonaws.bedrockruntime#InternalServerException" + }, + { + "target": "com.amazonaws.bedrockruntime#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.bedrockruntime#ServiceQuotaExceededException" + }, + { + "target": "com.amazonaws.bedrockruntime#ServiceUnavailableException" + }, + { + "target": "com.amazonaws.bedrockruntime#ThrottlingException" + }, + { + "target": "com.amazonaws.bedrockruntime#ValidationException" + } + ], + "traits": { + "smithy.api#documentation": "

The action to apply a guardrail.

For troubleshooting some of the common errors you might encounter when using the ApplyGuardrail API, see Troubleshooting Amazon Bedrock API Error Codes in the Amazon Bedrock User Guide

", + "smithy.api#http": { + "code": 200, + "method": "POST", + "uri": "/guardrail/{guardrailIdentifier}/version/{guardrailVersion}/apply" + } + } + }, + "com.amazonaws.bedrockruntime#ApplyGuardrailRequest": { + "type": "structure", + "members": { + "guardrailIdentifier": { + "target": "com.amazonaws.bedrockruntime#GuardrailIdentifier", + "traits": { + "smithy.api#documentation": "

The guardrail identifier used in the request to apply the guardrail.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "guardrailVersion": { + "target": "com.amazonaws.bedrockruntime#GuardrailVersion", + "traits": { + "smithy.api#documentation": "

The guardrail version used in the request to apply the guardrail.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "source": { + "target": "com.amazonaws.bedrockruntime#GuardrailContentSource", + "traits": { + "smithy.api#documentation": "

The source of data used in the request to apply the guardrail.

", + "smithy.api#required": {} + } + }, + "content": { + "target": "com.amazonaws.bedrockruntime#GuardrailContentBlockList", + "traits": { + "smithy.api#documentation": "

The content details used in the request to apply the guardrail.

", + "smithy.api#required": {} + } + }, + "outputScope": { + "target": "com.amazonaws.bedrockruntime#GuardrailOutputScope", + "traits": { + "smithy.api#documentation": "

Specifies the scope of the output that you get in the response. Set to FULL to return the entire output, including any detected and non-detected entries in the response for enhanced debugging.

Note that the full output scope doesn't apply to word filters or regex in sensitive information filters. It does apply to all other filtering policies, including sensitive information with filters that can detect personally identifiable information (PII).

" + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.bedrockruntime#ApplyGuardrailResponse": { + "type": "structure", + "members": { + "usage": { + "target": "com.amazonaws.bedrockruntime#GuardrailUsage", + "traits": { + "smithy.api#documentation": "

The usage details in the response from the guardrail.

", + "smithy.api#required": {} + } + }, + "action": { + "target": "com.amazonaws.bedrockruntime#GuardrailAction", + "traits": { + "smithy.api#documentation": "

The action taken in the response from the guardrail.

", + "smithy.api#required": {} + } + }, + "actionReason": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

The reason for the action taken when harmful content is detected.

" + } + }, + "outputs": { + "target": "com.amazonaws.bedrockruntime#GuardrailOutputContentList", + "traits": { + "smithy.api#documentation": "

The output details in the response from the guardrail.

", + "smithy.api#required": {} + } + }, + "assessments": { + "target": "com.amazonaws.bedrockruntime#GuardrailAssessmentList", + "traits": { + "smithy.api#documentation": "

The assessment details in the response from the guardrail.

", + "smithy.api#required": {} + } + }, + "guardrailCoverage": { + "target": "com.amazonaws.bedrockruntime#GuardrailCoverage", + "traits": { + "smithy.api#documentation": "

The guardrail coverage details in the apply guardrail response.

" + } + } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.bedrockruntime#AsyncInvokeArn": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 2048 + }, + "smithy.api#pattern": "^arn:[a-z0-9\\-]+:bedrock:[a-z0-9\\-]*:[0-9]*:(provisioned-model|foundation-model)/.+$" + } + }, + "com.amazonaws.bedrockruntime#AsyncInvokeIdempotencyToken": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 256 + }, + "smithy.api#pattern": "^[!-~]*$" + } + }, + "com.amazonaws.bedrockruntime#AsyncInvokeIdentifier": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 256 + }, + "smithy.api#pattern": "^[a-zA-Z_\\.\\-/0-9:]+$" + } + }, + "com.amazonaws.bedrockruntime#AsyncInvokeMessage": { + "type": "string", + "traits": { + "smithy.api#length": { + "max": 2048 + }, + "smithy.api#sensitive": {} + } + }, + "com.amazonaws.bedrockruntime#AsyncInvokeOutputDataConfig": { + "type": "union", + "members": { + "s3OutputDataConfig": { + "target": "com.amazonaws.bedrockruntime#AsyncInvokeS3OutputDataConfig", + "traits": { + "smithy.api#documentation": "

A storage location for the output data in an S3 bucket

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Asynchronous invocation output data settings.

" + } + }, + "com.amazonaws.bedrockruntime#AsyncInvokeResource": { + "type": "resource", + "operations": [ + { + "target": "com.amazonaws.bedrockruntime#GetAsyncInvoke" + }, + { + "target": "com.amazonaws.bedrockruntime#ListAsyncInvokes" + }, + { + "target": "com.amazonaws.bedrockruntime#StartAsyncInvoke" + } + ] + }, + "com.amazonaws.bedrockruntime#AsyncInvokeS3OutputDataConfig": { + "type": "structure", + "members": { + "s3Uri": { + "target": "com.amazonaws.bedrockruntime#S3Uri", + "traits": { + "smithy.api#documentation": "

An object URI starting with s3://.

", + "smithy.api#required": {} + } + }, + "kmsKeyId": { + "target": "com.amazonaws.bedrockruntime#KmsKeyId", + "traits": { + "smithy.api#documentation": "

A KMS encryption key ID.

" + } + }, + "bucketOwner": { + "target": "com.amazonaws.bedrockruntime#AccountId", + "traits": { + "smithy.api#documentation": "

If the bucket belongs to another AWS account, specify that account's ID.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Asynchronous invocation output data settings.

" + } + }, + "com.amazonaws.bedrockruntime#AsyncInvokeStatus": { + "type": "enum", + "members": { + "IN_PROGRESS": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "InProgress" + } + }, + "COMPLETED": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "Completed" + } + }, + "FAILED": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "Failed" + } + } + } + }, + "com.amazonaws.bedrockruntime#AsyncInvokeSummaries": { + "type": "list", + "member": { + "target": "com.amazonaws.bedrockruntime#AsyncInvokeSummary" + } + }, + "com.amazonaws.bedrockruntime#AsyncInvokeSummary": { + "type": "structure", + "members": { + "invocationArn": { + "target": "com.amazonaws.bedrockruntime#InvocationArn", + "traits": { + "smithy.api#documentation": "

The invocation's ARN.

", + "smithy.api#required": {} + } + }, + "modelArn": { + "target": "com.amazonaws.bedrockruntime#AsyncInvokeArn", + "traits": { + "smithy.api#documentation": "

The invoked model's ARN.

", + "smithy.api#required": {} + } + }, + "clientRequestToken": { + "target": "com.amazonaws.bedrockruntime#AsyncInvokeIdempotencyToken", + "traits": { + "smithy.api#documentation": "

The invocation's idempotency token.

" + } + }, + "status": { + "target": "com.amazonaws.bedrockruntime#AsyncInvokeStatus", + "traits": { + "smithy.api#documentation": "

The invocation's status.

" + } + }, + "failureMessage": { + "target": "com.amazonaws.bedrockruntime#AsyncInvokeMessage", + "traits": { + "smithy.api#documentation": "

An error message.

" + } + }, + "submitTime": { + "target": "com.amazonaws.bedrockruntime#Timestamp", + "traits": { + "smithy.api#documentation": "

When the invocation was submitted.

", + "smithy.api#required": {} + } + }, + "lastModifiedTime": { + "target": "com.amazonaws.bedrockruntime#Timestamp", + "traits": { + "smithy.api#documentation": "

When the invocation was last modified.

" + } + }, + "endTime": { + "target": "com.amazonaws.bedrockruntime#Timestamp", + "traits": { + "smithy.api#documentation": "

When the invocation ended.

" + } + }, + "outputDataConfig": { + "target": "com.amazonaws.bedrockruntime#AsyncInvokeOutputDataConfig", + "traits": { + "smithy.api#documentation": "

The invocation's output data settings.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

A summary of an asynchronous invocation.

" + } + }, + "com.amazonaws.bedrockruntime#AudioBlock": { + "type": "structure", + "members": { + "format": { + "target": "com.amazonaws.bedrockruntime#AudioFormat", + "traits": { + "smithy.api#documentation": "

The format of the audio data, such as MP3, WAV, FLAC, or other supported audio formats.

", + "smithy.api#required": {} + } + }, + "source": { + "target": "com.amazonaws.bedrockruntime#AudioSource", + "traits": { + "smithy.api#documentation": "

The source of the audio data, which can be provided as raw bytes or an S3 location.

", + "smithy.api#required": {} + } + }, + "error": { + "target": "com.amazonaws.bedrockruntime#ErrorBlock", + "traits": { + "smithy.api#documentation": "

Error information if the audio block could not be processed or contains invalid data.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

An audio content block that contains audio data in various supported formats.

" + } + }, + "com.amazonaws.bedrockruntime#AudioFormat": { + "type": "enum", + "members": { + "MP3": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "mp3" + } + }, + "OPUS": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "opus" + } + }, + "WAV": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "wav" + } + }, + "AAC": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "aac" + } + }, + "FLAC": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "flac" + } + }, + "MP4": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "mp4" + } + }, + "OGG": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "ogg" + } + }, + "MKV": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "mkv" + } + }, + "MKA": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "mka" + } + }, + "X_AAC": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "x-aac" + } + }, + "M4A": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "m4a" + } + }, + "MPEG": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "mpeg" + } + }, + "MPGA": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "mpga" + } + }, + "PCM": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "pcm" + } + }, + "WEBM": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "webm" + } + } + } + }, + "com.amazonaws.bedrockruntime#AudioSource": { + "type": "union", + "members": { + "bytes": { + "target": "smithy.api#Blob", + "traits": { + "smithy.api#documentation": "

Audio data encoded in base64.

", + "smithy.api#length": { + "min": 1 + } + } + }, + "s3Location": { + "target": "com.amazonaws.bedrockruntime#S3Location", + "traits": { + "smithy.api#documentation": "

A reference to audio data stored in an Amazon S3 bucket. To see which models support S3 uploads, see Supported models and features for Converse.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The source of audio data, which can be provided either as raw bytes or a reference to an S3 location.

", + "smithy.api#sensitive": {} + } + }, + "com.amazonaws.bedrockruntime#AutoToolChoice": { + "type": "structure", + "members": {}, + "traits": { + "smithy.api#documentation": "

The Model automatically decides if a tool should be called or whether to generate text instead. For example, {\"auto\" : {}}. For more information, see Call a tool with the Converse API in the Amazon Bedrock User Guide

" + } + }, + "com.amazonaws.bedrockruntime#AutomatedReasoningRuleIdentifier": { + "type": "string", + "traits": { + "smithy.api#length": { + "max": 12 + }, + "smithy.api#pattern": "^[a-z0-9]{12}$" + } + }, + "com.amazonaws.bedrockruntime#BidirectionalInputPayloadPart": { + "type": "structure", + "members": { + "bytes": { + "target": "com.amazonaws.bedrockruntime#PartBody", + "traits": { + "smithy.api#documentation": "

The audio content for the bidirectional input.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Payload content for the bidirectional input. The input is an audio stream.

", + "smithy.api#sensitive": {} + } + }, + "com.amazonaws.bedrockruntime#BidirectionalOutputPayloadPart": { + "type": "structure", + "members": { + "bytes": { + "target": "com.amazonaws.bedrockruntime#PartBody", + "traits": { + "smithy.api#documentation": "

The speech output of the bidirectional stream.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Output from the bidirectional stream. The output is speech and a text transcription.

", + "smithy.api#sensitive": {} + } + }, + "com.amazonaws.bedrockruntime#Body": { + "type": "blob", + "traits": { + "smithy.api#length": { + "max": 25000000 + }, + "smithy.api#sensitive": {} + } + }, + "com.amazonaws.bedrockruntime#CacheDetail": { + "type": "structure", + "members": { + "ttl": { + "target": "com.amazonaws.bedrockruntime#CacheTTL", + "traits": { + "smithy.api#documentation": "

TTL duration for these cached tokens

", + "smithy.api#required": {} + } + }, + "inputTokens": { + "target": "smithy.api#Integer", + "traits": { + "smithy.api#documentation": "

Number of tokens written to cache with this TTL (cache creation tokens)

", + "smithy.api#range": { + "min": 0 + }, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Cache creation metrics for a specific TTL duration

" + } + }, + "com.amazonaws.bedrockruntime#CacheDetailsList": { + "type": "list", + "member": { + "target": "com.amazonaws.bedrockruntime#CacheDetail" + }, + "traits": { + "smithy.api#documentation": "

List of cache details by TTL

" + } + }, + "com.amazonaws.bedrockruntime#CachePointBlock": { + "type": "structure", + "members": { + "type": { + "target": "com.amazonaws.bedrockruntime#CachePointType", + "traits": { + "smithy.api#documentation": "

Specifies the type of cache point within the CachePointBlock.

", + "smithy.api#required": {} + } + }, + "ttl": { + "target": "com.amazonaws.bedrockruntime#CacheTTL", + "traits": { + "smithy.api#documentation": "

Optional TTL duration for cache entries. When specified, enables extended TTL caching with the specified duration. When omitted, uses type value for caching behavior.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Defines a section of content to be cached for reuse in subsequent API calls.

" + } + }, + "com.amazonaws.bedrockruntime#CachePointType": { + "type": "enum", + "members": { + "DEFAULT": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "default" + } + } + } + }, + "com.amazonaws.bedrockruntime#CacheTTL": { + "type": "enum", + "members": { + "FIVE_MINUTES": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "5m" + } + }, + "ONE_HOUR": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "1h" + } + } + }, + "traits": { + "smithy.api#documentation": "

Time-to-live duration for ephemeral cache entries

" + } + }, + "com.amazonaws.bedrockruntime#Citation": { + "type": "structure", + "members": { + "title": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

The title or identifier of the source document being cited.

" + } + }, + "source": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

The source from the original search result that provided the cited content.

" + } + }, + "sourceContent": { + "target": "com.amazonaws.bedrockruntime#CitationSourceContentList", + "traits": { + "smithy.api#documentation": "

The specific content from the source document that was referenced or cited in the generated response.

" + } + }, + "location": { + "target": "com.amazonaws.bedrockruntime#CitationLocation", + "traits": { + "smithy.api#documentation": "

The precise location within the source document where the cited content can be found, including character positions, page numbers, or chunk identifiers.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Contains information about a citation that references a specific source document. Citations provide traceability between the model's generated response and the source documents that informed that response.

" + } + }, + "com.amazonaws.bedrockruntime#CitationGeneratedContent": { + "type": "union", + "members": { + "text": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

The text content that was generated by the model and is supported by the associated citation.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Contains the generated text content that corresponds to or is supported by a citation from a source document.

" + } + }, + "com.amazonaws.bedrockruntime#CitationGeneratedContentList": { + "type": "list", + "member": { + "target": "com.amazonaws.bedrockruntime#CitationGeneratedContent" + } + }, + "com.amazonaws.bedrockruntime#CitationLocation": { + "type": "union", + "members": { + "web": { + "target": "com.amazonaws.bedrockruntime#WebLocation", + "traits": { + "smithy.api#documentation": "

The web URL that was cited for this reference.

" + } + }, + "documentChar": { + "target": "com.amazonaws.bedrockruntime#DocumentCharLocation", + "traits": { + "smithy.api#documentation": "

The character-level location within the document where the cited content is found.

" + } + }, + "documentPage": { + "target": "com.amazonaws.bedrockruntime#DocumentPageLocation", + "traits": { + "smithy.api#documentation": "

The page-level location within the document where the cited content is found.

" + } + }, + "documentChunk": { + "target": "com.amazonaws.bedrockruntime#DocumentChunkLocation", + "traits": { + "smithy.api#documentation": "

The chunk-level location within the document where the cited content is found, typically used for documents that have been segmented into logical chunks.

" + } + }, + "searchResultLocation": { + "target": "com.amazonaws.bedrockruntime#SearchResultLocation", + "traits": { + "smithy.api#documentation": "

The search result location where the cited content is found, including the search result index and block positions within the content array.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Specifies the precise location within a source document where cited content can be found. This can include character-level positions, page numbers, or document chunks depending on the document type and indexing method.

" + } + }, + "com.amazonaws.bedrockruntime#CitationSourceContent": { + "type": "union", + "members": { + "text": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

The text content from the source document that is being cited.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Contains the actual text content from a source document that is being cited or referenced in the model's response.

" + } + }, + "com.amazonaws.bedrockruntime#CitationSourceContentDelta": { + "type": "structure", + "members": { + "text": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

An incremental update to the text content from the source document that is being cited.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Contains incremental updates to the source content text during streaming responses, allowing clients to build up the cited content progressively.

" + } + }, + "com.amazonaws.bedrockruntime#CitationSourceContentList": { + "type": "list", + "member": { + "target": "com.amazonaws.bedrockruntime#CitationSourceContent" + } + }, + "com.amazonaws.bedrockruntime#CitationSourceContentListDelta": { + "type": "list", + "member": { + "target": "com.amazonaws.bedrockruntime#CitationSourceContentDelta" + } + }, + "com.amazonaws.bedrockruntime#Citations": { + "type": "list", + "member": { + "target": "com.amazonaws.bedrockruntime#Citation" + } + }, + "com.amazonaws.bedrockruntime#CitationsConfig": { + "type": "structure", + "members": { + "enabled": { + "target": "smithy.api#Boolean", + "traits": { + "smithy.api#documentation": "

Specifies whether citations from the selected document should be used in the model's response. When set to true, the model can generate citations that reference the source documents used to inform the response.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Configuration settings for enabling and controlling document citations in Converse API responses. When enabled, the model can include citation information that links generated content back to specific source documents.

" + } + }, + "com.amazonaws.bedrockruntime#CitationsContentBlock": { + "type": "structure", + "members": { + "content": { + "target": "com.amazonaws.bedrockruntime#CitationGeneratedContentList", + "traits": { + "smithy.api#documentation": "

The generated content that is supported by the associated citations.

" + } + }, + "citations": { + "target": "com.amazonaws.bedrockruntime#Citations", + "traits": { + "smithy.api#documentation": "

An array of citations that reference the source documents used to generate the associated content.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A content block that contains both generated text and associated citation information. This block type is returned when document citations are enabled, providing traceability between the generated content and the source documents that informed the response.

" + } + }, + "com.amazonaws.bedrockruntime#CitationsDelta": { + "type": "structure", + "members": { + "title": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

The title or identifier of the source document being cited.

" + } + }, + "source": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

The source from the original search result that provided the cited content.

" + } + }, + "sourceContent": { + "target": "com.amazonaws.bedrockruntime#CitationSourceContentListDelta", + "traits": { + "smithy.api#documentation": "

The specific content from the source document that was referenced or cited in the generated response.

" + } + }, + "location": { + "target": "com.amazonaws.bedrockruntime#CitationLocation", + "traits": { + "smithy.api#documentation": "

Specifies the precise location within a source document where cited content can be found. This can include character-level positions, page numbers, or document chunks depending on the document type and indexing method.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Contains incremental updates to citation information during streaming responses. This allows clients to build up citation data progressively as the response is generated.

" + } + }, + "com.amazonaws.bedrockruntime#ConflictException": { + "type": "structure", + "members": { + "message": { + "target": "com.amazonaws.bedrockruntime#NonBlankString" + } + }, + "traits": { + "smithy.api#documentation": "

Error occurred because of a conflict while performing an operation.

", + "smithy.api#error": "client", + "smithy.api#httpError": 400 + } + }, + "com.amazonaws.bedrockruntime#ContentBlock": { + "type": "union", + "members": { + "text": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

Text to include in the message.

" + } + }, + "image": { + "target": "com.amazonaws.bedrockruntime#ImageBlock", + "traits": { + "smithy.api#documentation": "

Image to include in the message.

This field is only supported by Anthropic Claude 3 models.

" + } + }, + "document": { + "target": "com.amazonaws.bedrockruntime#DocumentBlock", + "traits": { + "smithy.api#documentation": "

A document to include in the message.

" + } + }, + "video": { + "target": "com.amazonaws.bedrockruntime#VideoBlock", + "traits": { + "smithy.api#documentation": "

Video to include in the message.

" + } + }, + "audio": { + "target": "com.amazonaws.bedrockruntime#AudioBlock", + "traits": { + "smithy.api#documentation": "

An audio content block containing audio data in the conversation.

" + } + }, + "toolUse": { + "target": "com.amazonaws.bedrockruntime#ToolUseBlock", + "traits": { + "smithy.api#documentation": "

Information about a tool use request from a model.

" + } + }, + "toolResult": { + "target": "com.amazonaws.bedrockruntime#ToolResultBlock", + "traits": { + "smithy.api#documentation": "

The result for a tool request that a model makes.

" + } + }, + "guardContent": { + "target": "com.amazonaws.bedrockruntime#GuardrailConverseContentBlock", + "traits": { + "smithy.api#documentation": "

Contains the content to assess with the guardrail. If you don't specify guardContent in a call to the Converse API, the guardrail (if passed in the Converse API) assesses the entire message.

For more information, see Use a guardrail with the Converse API in the Amazon Bedrock User Guide.

" + } + }, + "cachePoint": { + "target": "com.amazonaws.bedrockruntime#CachePointBlock", + "traits": { + "smithy.api#documentation": "

CachePoint to include in the message.

" + } + }, + "reasoningContent": { + "target": "com.amazonaws.bedrockruntime#ReasoningContentBlock", + "traits": { + "smithy.api#documentation": "

Contains content regarding the reasoning that is carried out by the model. Reasoning refers to a Chain of Thought (CoT) that the model generates to enhance the accuracy of its final response.

" + } + }, + "citationsContent": { + "target": "com.amazonaws.bedrockruntime#CitationsContentBlock", + "traits": { + "smithy.api#documentation": "

A content block that contains both generated text and associated citation information, providing traceability between the response and source documents.

" + } + }, + "searchResult": { + "target": "com.amazonaws.bedrockruntime#SearchResultBlock", + "traits": { + "smithy.api#documentation": "

Search result to include in the message.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A block of content for a message that you pass to, or receive from, a model with the Converse or ConverseStream API operations.

" + } + }, + "com.amazonaws.bedrockruntime#ContentBlockDelta": { + "type": "union", + "members": { + "text": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

The content text.

" + } + }, + "toolUse": { + "target": "com.amazonaws.bedrockruntime#ToolUseBlockDelta", + "traits": { + "smithy.api#documentation": "

Information about a tool that the model is requesting to use.

" + } + }, + "toolResult": { + "target": "com.amazonaws.bedrockruntime#ToolResultBlocksDelta", + "traits": { + "smithy.api#documentation": "

An incremental update that contains the results from a tool call.

" + } + }, + "reasoningContent": { + "target": "com.amazonaws.bedrockruntime#ReasoningContentBlockDelta", + "traits": { + "smithy.api#documentation": "

Contains content regarding the reasoning that is carried out by the model. Reasoning refers to a Chain of Thought (CoT) that the model generates to enhance the accuracy of its final response.

" + } + }, + "citation": { + "target": "com.amazonaws.bedrockruntime#CitationsDelta", + "traits": { + "smithy.api#documentation": "

Incremental citation information that is streamed as part of the response generation process.

" + } + }, + "image": { + "target": "com.amazonaws.bedrockruntime#ImageBlockDelta", + "traits": { + "smithy.api#documentation": "

A streaming delta event containing incremental image data.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A block of content in a streaming response.

" + } + }, + "com.amazonaws.bedrockruntime#ContentBlockDeltaEvent": { + "type": "structure", + "members": { + "delta": { + "target": "com.amazonaws.bedrockruntime#ContentBlockDelta", + "traits": { + "smithy.api#documentation": "

The delta for a content block delta event.

", + "smithy.api#required": {} + } + }, + "contentBlockIndex": { + "target": "com.amazonaws.bedrockruntime#NonNegativeInteger", + "traits": { + "smithy.api#documentation": "

The block index for a content block delta event.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

The content block delta event.

" + } + }, + "com.amazonaws.bedrockruntime#ContentBlockStart": { + "type": "union", + "members": { + "toolUse": { + "target": "com.amazonaws.bedrockruntime#ToolUseBlockStart", + "traits": { + "smithy.api#documentation": "

Information about a tool that the model is requesting to use.

" + } + }, + "toolResult": { + "target": "com.amazonaws.bedrockruntime#ToolResultBlockStart", + "traits": { + "smithy.api#documentation": "

The

" + } + }, + "image": { + "target": "com.amazonaws.bedrockruntime#ImageBlockStart", + "traits": { + "smithy.api#documentation": "

The initial event indicating the start of a streaming image block.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Content block start information.

" + } + }, + "com.amazonaws.bedrockruntime#ContentBlockStartEvent": { + "type": "structure", + "members": { + "start": { + "target": "com.amazonaws.bedrockruntime#ContentBlockStart", + "traits": { + "smithy.api#documentation": "

Start information about a content block start event.

", + "smithy.api#required": {} + } + }, + "contentBlockIndex": { + "target": "com.amazonaws.bedrockruntime#NonNegativeInteger", + "traits": { + "smithy.api#documentation": "

The index for a content block start event.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Content block start event.

" + } + }, + "com.amazonaws.bedrockruntime#ContentBlockStopEvent": { + "type": "structure", + "members": { + "contentBlockIndex": { + "target": "com.amazonaws.bedrockruntime#NonNegativeInteger", + "traits": { + "smithy.api#documentation": "

The index for a content block.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

A content block stop event.

" + } + }, + "com.amazonaws.bedrockruntime#ContentBlocks": { + "type": "list", + "member": { + "target": "com.amazonaws.bedrockruntime#ContentBlock" + } + }, + "com.amazonaws.bedrockruntime#ConversationRole": { + "type": "enum", + "members": { + "USER": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "user" + } + }, + "ASSISTANT": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "assistant" + } + } + } + }, + "com.amazonaws.bedrockruntime#ConversationalModelId": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 2048 + }, + "smithy.api#pattern": "^(arn:aws(-[^:]+)?:bedrock:[a-z0-9-]{1,20}:(([0-9]{12}:custom-model/[a-z0-9-]{1,63}[.]{1}[a-z0-9-]{1,63}/[a-z0-9]{12})|(:foundation-model/[a-z0-9-]{1,63}[.]{1}[a-z0-9-]{1,63}([.:]?[a-z0-9-]{1,63}))|([0-9]{12}:imported-model/[a-z0-9]{12})|([0-9]{12}:provisioned-model/[a-z0-9]{12})|([0-9]{12}:custom-model-deployment/[a-z0-9]{12})|([0-9]{12}:(inference-profile|application-inference-profile)/[a-zA-Z0-9-:.]+)))|([a-z0-9-]{1,63}[.]{1}[a-z0-9-]{1,63}([.:]?[a-z0-9-]{1,63}))|(([0-9a-zA-Z][_-]?)+)|([a-zA-Z0-9-:.]+)|(^(arn:aws(-[^:]+)?:bedrock:[a-z0-9-]{1,20}:[0-9]{12}:prompt/[0-9a-zA-Z]{10}(?::[0-9]{1,5})?))$|(^arn:aws:sagemaker:[a-z0-9-]+:[0-9]{12}:endpoint/[a-zA-Z0-9-]+$)|(^arn:aws(-[^:]+)?:bedrock:([0-9a-z-]{1,20}):([0-9]{12}):(default-)?prompt-router/[a-zA-Z0-9-:.]+$)$" + } + }, + "com.amazonaws.bedrockruntime#Converse": { + "type": "operation", + "input": { + "target": "com.amazonaws.bedrockruntime#ConverseRequest" + }, + "output": { + "target": "com.amazonaws.bedrockruntime#ConverseResponse" + }, + "errors": [ + { + "target": "com.amazonaws.bedrockruntime#AccessDeniedException" + }, + { + "target": "com.amazonaws.bedrockruntime#InternalServerException" + }, + { + "target": "com.amazonaws.bedrockruntime#ModelErrorException" + }, + { + "target": "com.amazonaws.bedrockruntime#ModelNotReadyException" + }, + { + "target": "com.amazonaws.bedrockruntime#ModelTimeoutException" + }, + { + "target": "com.amazonaws.bedrockruntime#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.bedrockruntime#ServiceUnavailableException" + }, + { + "target": "com.amazonaws.bedrockruntime#ThrottlingException" + }, + { + "target": "com.amazonaws.bedrockruntime#ValidationException" + } + ], + "traits": { + "smithy.api#documentation": "

Sends messages to the specified Amazon Bedrock model. Converse provides a consistent interface that works with all models that support messages. This allows you to write code once and use it with different models. If a model has unique inference parameters, you can also pass those unique parameters to the model.

Amazon Bedrock doesn't store any text, images, or documents that you provide as content. The data is only used to generate the response.

You can submit a prompt by including it in the messages field, specifying the modelId of a foundation model or inference profile to run inference on it, and including any other fields that are relevant to your use case.

You can also submit a prompt from Prompt management by specifying the ARN of the prompt version and including a map of variables to values in the promptVariables field. You can append more messages to the prompt by using the messages field. If you use a prompt from Prompt management, you can't include the following fields in the request: additionalModelRequestFields, inferenceConfig, system, or toolConfig. Instead, these fields must be defined through Prompt management. For more information, see Use a prompt from Prompt management.

For information about the Converse API, see Use the Converse API in the Amazon Bedrock User Guide. To use a guardrail, see Use a guardrail with the Converse API in the Amazon Bedrock User Guide. To use a tool with a model, see Tool use (Function calling) in the Amazon Bedrock User Guide

For example code, see Converse API examples in the Amazon Bedrock User Guide.

This operation requires permission for the bedrock:InvokeModel action.

To deny all inference access to resources that you specify in the modelId field, you need to deny access to the bedrock:InvokeModel and bedrock:InvokeModelWithResponseStream actions. Doing this also denies access to the resource through the base inference actions (InvokeModel and InvokeModelWithResponseStream). For more information see Deny access for inference on specific models.

For troubleshooting some of the common errors you might encounter when using the Converse API, see Troubleshooting Amazon Bedrock API Error Codes in the Amazon Bedrock User Guide

", + "smithy.api#http": { + "code": 200, + "method": "POST", + "uri": "/model/{modelId}/converse" + } + } + }, + "com.amazonaws.bedrockruntime#ConverseMetrics": { + "type": "structure", + "members": { + "latencyMs": { + "target": "smithy.api#Long", + "traits": { + "smithy.api#documentation": "

The latency of the call to Converse, in milliseconds.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Metrics for a call to Converse.

" + } + }, + "com.amazonaws.bedrockruntime#ConverseOutput": { + "type": "union", + "members": { + "message": { + "target": "com.amazonaws.bedrockruntime#Message", + "traits": { + "smithy.api#documentation": "

The message that the model generates.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The output from a call to Converse.

" + } + }, + "com.amazonaws.bedrockruntime#ConverseRequest": { + "type": "structure", + "members": { + "modelId": { + "target": "com.amazonaws.bedrockruntime#ConversationalModelId", + "traits": { + "smithy.api#documentation": "

Specifies the model or throughput with which to run inference, or the prompt resource to use in inference. The value depends on the resource that you use:

The Converse API doesn't support imported models.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "messages": { + "target": "com.amazonaws.bedrockruntime#Messages", + "traits": { + "smithy.api#documentation": "

The messages that you want to send to the model.

" + } + }, + "system": { + "target": "com.amazonaws.bedrockruntime#SystemContentBlocks", + "traits": { + "smithy.api#documentation": "

A prompt that provides instructions or context to the model about the task it should perform, or the persona it should adopt during the conversation.

" + } + }, + "inferenceConfig": { + "target": "com.amazonaws.bedrockruntime#InferenceConfiguration", + "traits": { + "smithy.api#documentation": "

Inference parameters to pass to the model. Converse and ConverseStream support a base set of inference parameters. If you need to pass additional parameters that the model supports, use the additionalModelRequestFields request field.

" + } + }, + "toolConfig": { + "target": "com.amazonaws.bedrockruntime#ToolConfiguration", + "traits": { + "smithy.api#documentation": "

Configuration information for the tools that the model can use when generating a response.

For information about models that support tool use, see Supported models and model features.

" + } + }, + "guardrailConfig": { + "target": "com.amazonaws.bedrockruntime#GuardrailConfiguration", + "traits": { + "smithy.api#documentation": "

Configuration information for a guardrail that you want to use in the request. If you include guardContent blocks in the content field in the messages field, the guardrail operates only on those messages. If you include no guardContent blocks, the guardrail operates on all messages in the request body and in any included prompt resource.

" + } + }, + "additionalModelRequestFields": { + "target": "smithy.api#Document", + "traits": { + "smithy.api#documentation": "

Additional inference parameters that the model supports, beyond the base set of inference parameters that Converse and ConverseStream support in the inferenceConfig field. For more information, see Model parameters.

" + } + }, + "promptVariables": { + "target": "com.amazonaws.bedrockruntime#PromptVariableMap", + "traits": { + "smithy.api#documentation": "

Contains a map of variables in a prompt from Prompt management to objects containing the values to fill in for them when running model invocation. This field is ignored if you don't specify a prompt resource in the modelId field.

" + } + }, + "additionalModelResponseFieldPaths": { + "target": "com.amazonaws.bedrockruntime#AdditionalModelResponseFieldPaths", + "traits": { + "smithy.api#documentation": "

Additional model parameters field paths to return in the response. Converse and ConverseStream return the requested fields as a JSON Pointer object in the additionalModelResponseFields field. The following is example JSON for additionalModelResponseFieldPaths.

[ \"/stop_sequence\" ]

For information about the JSON Pointer syntax, see the Internet Engineering Task Force (IETF) documentation.

Converse and ConverseStream reject an empty JSON Pointer or incorrectly structured JSON Pointer with a 400 error code. if the JSON Pointer is valid, but the requested field is not in the model response, it is ignored by Converse.

", + "smithy.api#length": { + "max": 10 + } + } + }, + "requestMetadata": { + "target": "com.amazonaws.bedrockruntime#RequestMetadata", + "traits": { + "smithy.api#documentation": "

Key-value pairs that you can use to filter invocation logs.

" + } + }, + "performanceConfig": { + "target": "com.amazonaws.bedrockruntime#PerformanceConfiguration", + "traits": { + "smithy.api#documentation": "

Model performance settings for the request.

" + } + }, + "serviceTier": { + "target": "com.amazonaws.bedrockruntime#ServiceTier", + "traits": { + "smithy.api#documentation": "

Specifies the processing tier configuration used for serving the request.

" + } + }, + "outputConfig": { + "target": "com.amazonaws.bedrockruntime#OutputConfig", + "traits": { + "smithy.api#documentation": "

Output configuration for a model response.

" + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.bedrockruntime#ConverseResponse": { + "type": "structure", + "members": { + "output": { + "target": "com.amazonaws.bedrockruntime#ConverseOutput", + "traits": { + "smithy.api#documentation": "

The result from the call to Converse.

", + "smithy.api#required": {} + } + }, + "stopReason": { + "target": "com.amazonaws.bedrockruntime#StopReason", + "traits": { + "smithy.api#documentation": "

The reason why the model stopped generating output.

", + "smithy.api#required": {} + } + }, + "usage": { + "target": "com.amazonaws.bedrockruntime#TokenUsage", + "traits": { + "smithy.api#documentation": "

The total number of tokens used in the call to Converse. The total includes the tokens input to the model and the tokens generated by the model.

", + "smithy.api#required": {} + } + }, + "metrics": { + "target": "com.amazonaws.bedrockruntime#ConverseMetrics", + "traits": { + "smithy.api#documentation": "

Metrics for the call to Converse.

", + "smithy.api#required": {} + } + }, + "additionalModelResponseFields": { + "target": "smithy.api#Document", + "traits": { + "smithy.api#documentation": "

Additional fields in the response that are unique to the model.

" + } + }, + "trace": { + "target": "com.amazonaws.bedrockruntime#ConverseTrace", + "traits": { + "smithy.api#documentation": "

A trace object that contains information about the Guardrail behavior.

" + } + }, + "performanceConfig": { + "target": "com.amazonaws.bedrockruntime#PerformanceConfiguration", + "traits": { + "smithy.api#documentation": "

Model performance settings for the request.

" + } + }, + "serviceTier": { + "target": "com.amazonaws.bedrockruntime#ServiceTier", + "traits": { + "smithy.api#documentation": "

Specifies the processing tier configuration used for serving the request.

" + } + } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.bedrockruntime#ConverseStream": { + "type": "operation", + "input": { + "target": "com.amazonaws.bedrockruntime#ConverseStreamRequest" + }, + "output": { + "target": "com.amazonaws.bedrockruntime#ConverseStreamResponse" + }, + "errors": [ + { + "target": "com.amazonaws.bedrockruntime#AccessDeniedException" + }, + { + "target": "com.amazonaws.bedrockruntime#InternalServerException" + }, + { + "target": "com.amazonaws.bedrockruntime#ModelErrorException" + }, + { + "target": "com.amazonaws.bedrockruntime#ModelNotReadyException" + }, + { + "target": "com.amazonaws.bedrockruntime#ModelTimeoutException" + }, + { + "target": "com.amazonaws.bedrockruntime#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.bedrockruntime#ServiceUnavailableException" + }, + { + "target": "com.amazonaws.bedrockruntime#ThrottlingException" + }, + { + "target": "com.amazonaws.bedrockruntime#ValidationException" + } + ], + "traits": { + "smithy.api#documentation": "

Sends messages to the specified Amazon Bedrock model and returns the response in a stream. ConverseStream provides a consistent API that works with all Amazon Bedrock models that support messages. This allows you to write code once and use it with different models. Should a model have unique inference parameters, you can also pass those unique parameters to the model.

To find out if a model supports streaming, call GetFoundationModel and check the responseStreamingSupported field in the response.

The CLI doesn't support streaming operations in Amazon Bedrock, including ConverseStream.

Amazon Bedrock doesn't store any text, images, or documents that you provide as content. The data is only used to generate the response.

You can submit a prompt by including it in the messages field, specifying the modelId of a foundation model or inference profile to run inference on it, and including any other fields that are relevant to your use case.

You can also submit a prompt from Prompt management by specifying the ARN of the prompt version and including a map of variables to values in the promptVariables field. You can append more messages to the prompt by using the messages field. If you use a prompt from Prompt management, you can't include the following fields in the request: additionalModelRequestFields, inferenceConfig, system, or toolConfig. Instead, these fields must be defined through Prompt management. For more information, see Use a prompt from Prompt management.

For information about the Converse API, see Use the Converse API in the Amazon Bedrock User Guide. To use a guardrail, see Use a guardrail with the Converse API in the Amazon Bedrock User Guide. To use a tool with a model, see Tool use (Function calling) in the Amazon Bedrock User Guide

For example code, see Conversation streaming example in the Amazon Bedrock User Guide.

This operation requires permission for the bedrock:InvokeModelWithResponseStream action.

To deny all inference access to resources that you specify in the modelId field, you need to deny access to the bedrock:InvokeModel and bedrock:InvokeModelWithResponseStream actions. Doing this also denies access to the resource through the base inference actions (InvokeModel and InvokeModelWithResponseStream). For more information see Deny access for inference on specific models.

For troubleshooting some of the common errors you might encounter when using the ConverseStream API, see Troubleshooting Amazon Bedrock API Error Codes in the Amazon Bedrock User Guide

", + "smithy.api#http": { + "code": 200, + "method": "POST", + "uri": "/model/{modelId}/converse-stream" + } + } + }, + "com.amazonaws.bedrockruntime#ConverseStreamMetadataEvent": { + "type": "structure", + "members": { + "usage": { + "target": "com.amazonaws.bedrockruntime#TokenUsage", + "traits": { + "smithy.api#documentation": "

Usage information for the conversation stream event.

", + "smithy.api#required": {} + } + }, + "metrics": { + "target": "com.amazonaws.bedrockruntime#ConverseStreamMetrics", + "traits": { + "smithy.api#documentation": "

The metrics for the conversation stream metadata event.

", + "smithy.api#required": {} + } + }, + "trace": { + "target": "com.amazonaws.bedrockruntime#ConverseStreamTrace", + "traits": { + "smithy.api#documentation": "

The trace object in the response from ConverseStream that contains information about the guardrail behavior.

" + } + }, + "performanceConfig": { + "target": "com.amazonaws.bedrockruntime#PerformanceConfiguration", + "traits": { + "smithy.api#documentation": "

Model performance configuration metadata for the conversation stream event.

" + } + }, + "serviceTier": { + "target": "com.amazonaws.bedrockruntime#ServiceTier", + "traits": { + "smithy.api#documentation": "

Specifies the processing tier configuration used for serving the request.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A conversation stream metadata event.

" + } + }, + "com.amazonaws.bedrockruntime#ConverseStreamMetrics": { + "type": "structure", + "members": { + "latencyMs": { + "target": "smithy.api#Long", + "traits": { + "smithy.api#documentation": "

The latency for the streaming request, in milliseconds.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Metrics for the stream.

" + } + }, + "com.amazonaws.bedrockruntime#ConverseStreamOutput": { + "type": "union", + "members": { + "messageStart": { + "target": "com.amazonaws.bedrockruntime#MessageStartEvent", + "traits": { + "smithy.api#documentation": "

Message start information.

" + } + }, + "contentBlockStart": { + "target": "com.amazonaws.bedrockruntime#ContentBlockStartEvent", + "traits": { + "smithy.api#documentation": "

Start information for a content block.

" + } + }, + "contentBlockDelta": { + "target": "com.amazonaws.bedrockruntime#ContentBlockDeltaEvent", + "traits": { + "smithy.api#documentation": "

The messages output content block delta.

" + } + }, + "contentBlockStop": { + "target": "com.amazonaws.bedrockruntime#ContentBlockStopEvent", + "traits": { + "smithy.api#documentation": "

Stop information for a content block.

" + } + }, + "messageStop": { + "target": "com.amazonaws.bedrockruntime#MessageStopEvent", + "traits": { + "smithy.api#documentation": "

Message stop information.

" + } + }, + "metadata": { + "target": "com.amazonaws.bedrockruntime#ConverseStreamMetadataEvent", + "traits": { + "smithy.api#documentation": "

Metadata for the converse output stream.

" + } + }, + "internalServerException": { + "target": "com.amazonaws.bedrockruntime#InternalServerException", + "traits": { + "smithy.api#documentation": "

An internal server error occurred. Retry your request.

" + } + }, + "modelStreamErrorException": { + "target": "com.amazonaws.bedrockruntime#ModelStreamErrorException", + "traits": { + "smithy.api#documentation": "

A streaming error occurred. Retry your request.

" + } + }, + "validationException": { + "target": "com.amazonaws.bedrockruntime#ValidationException", + "traits": { + "smithy.api#documentation": "

The input fails to satisfy the constraints specified by Amazon Bedrock. For troubleshooting this error, see ValidationError in the Amazon Bedrock User Guide.

" + } + }, + "throttlingException": { + "target": "com.amazonaws.bedrockruntime#ThrottlingException", + "traits": { + "smithy.api#documentation": "

Your request was denied due to exceeding the account quotas for Amazon Bedrock. For troubleshooting this error, see ThrottlingException in the Amazon Bedrock User Guide.

" + } + }, + "serviceUnavailableException": { + "target": "com.amazonaws.bedrockruntime#ServiceUnavailableException", + "traits": { + "smithy.api#documentation": "

The service isn't currently available. For troubleshooting this error, see ServiceUnavailable in the Amazon Bedrock User Guide

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The messages output stream

", + "smithy.api#streaming": {} + } + }, + "com.amazonaws.bedrockruntime#ConverseStreamRequest": { + "type": "structure", + "members": { + "modelId": { + "target": "com.amazonaws.bedrockruntime#ConversationalModelId", + "traits": { + "smithy.api#documentation": "

Specifies the model or throughput with which to run inference, or the prompt resource to use in inference. The value depends on the resource that you use:

The Converse API doesn't support imported models.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "messages": { + "target": "com.amazonaws.bedrockruntime#Messages", + "traits": { + "smithy.api#documentation": "

The messages that you want to send to the model.

" + } + }, + "system": { + "target": "com.amazonaws.bedrockruntime#SystemContentBlocks", + "traits": { + "smithy.api#documentation": "

A prompt that provides instructions or context to the model about the task it should perform, or the persona it should adopt during the conversation.

" + } + }, + "inferenceConfig": { + "target": "com.amazonaws.bedrockruntime#InferenceConfiguration", + "traits": { + "smithy.api#documentation": "

Inference parameters to pass to the model. Converse and ConverseStream support a base set of inference parameters. If you need to pass additional parameters that the model supports, use the additionalModelRequestFields request field.

" + } + }, + "toolConfig": { + "target": "com.amazonaws.bedrockruntime#ToolConfiguration", + "traits": { + "smithy.api#documentation": "

Configuration information for the tools that the model can use when generating a response.

For information about models that support streaming tool use, see Supported models and model features.

" + } + }, + "guardrailConfig": { + "target": "com.amazonaws.bedrockruntime#GuardrailStreamConfiguration", + "traits": { + "smithy.api#documentation": "

Configuration information for a guardrail that you want to use in the request. If you include guardContent blocks in the content field in the messages field, the guardrail operates only on those messages. If you include no guardContent blocks, the guardrail operates on all messages in the request body and in any included prompt resource.

" + } + }, + "additionalModelRequestFields": { + "target": "smithy.api#Document", + "traits": { + "smithy.api#documentation": "

Additional inference parameters that the model supports, beyond the base set of inference parameters that Converse and ConverseStream support in the inferenceConfig field. For more information, see Model parameters.

" + } + }, + "promptVariables": { + "target": "com.amazonaws.bedrockruntime#PromptVariableMap", + "traits": { + "smithy.api#documentation": "

Contains a map of variables in a prompt from Prompt management to objects containing the values to fill in for them when running model invocation. This field is ignored if you don't specify a prompt resource in the modelId field.

" + } + }, + "additionalModelResponseFieldPaths": { + "target": "com.amazonaws.bedrockruntime#AdditionalModelResponseFieldPaths", + "traits": { + "smithy.api#documentation": "

Additional model parameters field paths to return in the response. Converse and ConverseStream return the requested fields as a JSON Pointer object in the additionalModelResponseFields field. The following is example JSON for additionalModelResponseFieldPaths.

[ \"/stop_sequence\" ]

For information about the JSON Pointer syntax, see the Internet Engineering Task Force (IETF) documentation.

Converse and ConverseStream reject an empty JSON Pointer or incorrectly structured JSON Pointer with a 400 error code. if the JSON Pointer is valid, but the requested field is not in the model response, it is ignored by Converse.

", + "smithy.api#length": { + "max": 10 + } + } + }, + "requestMetadata": { + "target": "com.amazonaws.bedrockruntime#RequestMetadata", + "traits": { + "smithy.api#documentation": "

Key-value pairs that you can use to filter invocation logs.

" + } + }, + "performanceConfig": { + "target": "com.amazonaws.bedrockruntime#PerformanceConfiguration", + "traits": { + "smithy.api#documentation": "

Model performance settings for the request.

" + } + }, + "serviceTier": { + "target": "com.amazonaws.bedrockruntime#ServiceTier", + "traits": { + "smithy.api#documentation": "

Specifies the processing tier configuration used for serving the request.

" + } + }, + "outputConfig": { + "target": "com.amazonaws.bedrockruntime#OutputConfig", + "traits": { + "smithy.api#documentation": "

Output configuration for a model response.

" + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.bedrockruntime#ConverseStreamResponse": { + "type": "structure", + "members": { + "stream": { + "target": "com.amazonaws.bedrockruntime#ConverseStreamOutput", + "traits": { + "smithy.api#documentation": "

The output stream that the model generated.

", + "smithy.api#httpPayload": {} + } + } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.bedrockruntime#ConverseStreamTrace": { + "type": "structure", + "members": { + "guardrail": { + "target": "com.amazonaws.bedrockruntime#GuardrailTraceAssessment", + "traits": { + "smithy.api#documentation": "

The guardrail trace object.

" + } + }, + "promptRouter": { + "target": "com.amazonaws.bedrockruntime#PromptRouterTrace", + "traits": { + "smithy.api#documentation": "

The request's prompt router.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The trace object in a response from ConverseStream.

" + } + }, + "com.amazonaws.bedrockruntime#ConverseTokensRequest": { + "type": "structure", + "members": { + "messages": { + "target": "com.amazonaws.bedrockruntime#Messages", + "traits": { + "smithy.api#documentation": "

An array of messages to count tokens for.

" + } + }, + "system": { + "target": "com.amazonaws.bedrockruntime#SystemContentBlocks", + "traits": { + "smithy.api#documentation": "

The system content blocks to count tokens for. System content provides instructions or context to the model about how it should behave or respond. The token count will include any system content provided.

" + } + }, + "toolConfig": { + "target": "com.amazonaws.bedrockruntime#ToolConfiguration", + "traits": { + "smithy.api#documentation": "

The toolConfig of Converse input request to count tokens for. Configuration information for the tools that the model can use when generating a response.

" + } + }, + "additionalModelRequestFields": { + "target": "smithy.api#Document", + "traits": { + "smithy.api#documentation": "

The additionalModelRequestFields of Converse input request to count tokens for. Use this field when you want to pass additional parameters that the model supports.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The inputs from a Converse API request for token counting.

This structure mirrors the input format for the Converse operation, allowing you to count tokens for conversation-based inference requests.

" + } + }, + "com.amazonaws.bedrockruntime#ConverseTrace": { + "type": "structure", + "members": { + "guardrail": { + "target": "com.amazonaws.bedrockruntime#GuardrailTraceAssessment", + "traits": { + "smithy.api#documentation": "

The guardrail trace object.

" + } + }, + "promptRouter": { + "target": "com.amazonaws.bedrockruntime#PromptRouterTrace", + "traits": { + "smithy.api#documentation": "

The request's prompt router.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The trace object in a response from Converse.

" + } + }, + "com.amazonaws.bedrockruntime#CountTokens": { + "type": "operation", + "input": { + "target": "com.amazonaws.bedrockruntime#CountTokensRequest" + }, + "output": { + "target": "com.amazonaws.bedrockruntime#CountTokensResponse" + }, + "errors": [ + { + "target": "com.amazonaws.bedrockruntime#AccessDeniedException" + }, + { + "target": "com.amazonaws.bedrockruntime#InternalServerException" + }, + { + "target": "com.amazonaws.bedrockruntime#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.bedrockruntime#ServiceUnavailableException" + }, + { + "target": "com.amazonaws.bedrockruntime#ThrottlingException" + }, + { + "target": "com.amazonaws.bedrockruntime#ValidationException" + } + ], + "traits": { + "smithy.api#documentation": "

Returns the token count for a given inference request. This operation helps you estimate token usage before sending requests to foundation models by returning the token count that would be used if the same input were sent to the model in an inference request.

Token counting is model-specific because different models use different tokenization strategies. The token count returned by this operation will match the token count that would be charged if the same input were sent to the model in an InvokeModel or Converse request.

You can use this operation to:

  • Estimate costs before sending inference requests.

  • Optimize prompts to fit within token limits.

  • Plan for token usage in your applications.

This operation accepts the same input formats as InvokeModel and Converse, allowing you to count tokens for both raw text inputs and structured conversation formats.

The following operations are related to CountTokens:

  • InvokeModel - Sends inference requests to foundation models

  • Converse - Sends conversation-based inference requests to foundation models

", + "smithy.api#http": { + "code": 200, + "method": "POST", + "uri": "/model/{modelId}/count-tokens" + }, + "smithy.api#readonly": {} + } + }, + "com.amazonaws.bedrockruntime#CountTokensInput": { + "type": "union", + "members": { + "invokeModel": { + "target": "com.amazonaws.bedrockruntime#InvokeModelTokensRequest", + "traits": { + "smithy.api#documentation": "

An InvokeModel request for which to count tokens. Use this field when you want to count tokens for a raw text input that would be sent to the InvokeModel operation.

" + } + }, + "converse": { + "target": "com.amazonaws.bedrockruntime#ConverseTokensRequest", + "traits": { + "smithy.api#documentation": "

A Converse request for which to count tokens. Use this field when you want to count tokens for a conversation-based input that would be sent to the Converse operation.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The input value for token counting. The value should be either an InvokeModel or Converse request body.

" + } + }, + "com.amazonaws.bedrockruntime#CountTokensRequest": { + "type": "structure", + "members": { + "modelId": { + "target": "com.amazonaws.bedrockruntime#FoundationModelVersionIdentifier", + "traits": { + "smithy.api#documentation": "

The unique identifier or ARN of the foundation model to use for token counting. Each model processes tokens differently, so the token count is specific to the model you specify.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "input": { + "target": "com.amazonaws.bedrockruntime#CountTokensInput", + "traits": { + "smithy.api#documentation": "

The input for which to count tokens. The structure of this parameter depends on whether you're counting tokens for an InvokeModel or Converse request:

  • For InvokeModel requests, provide the request body in the invokeModel field

  • For Converse requests, provide the messages and system content in the converse field

The input format must be compatible with the model specified in the modelId parameter.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.bedrockruntime#CountTokensResponse": { + "type": "structure", + "members": { + "inputTokens": { + "target": "smithy.api#Integer", + "traits": { + "smithy.api#documentation": "

The number of tokens in the provided input according to the specified model's tokenization rules. This count represents the number of input tokens that would be processed if the same input were sent to the model in an inference request. Use this value to estimate costs and ensure your inputs stay within model token limits.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.bedrockruntime#DocumentBlock": { + "type": "structure", + "members": { + "format": { + "target": "com.amazonaws.bedrockruntime#DocumentFormat", + "traits": { + "smithy.api#addedDefault": {}, + "smithy.api#default": "txt", + "smithy.api#documentation": "

The format of a document, or its extension.

" + } + }, + "name": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

A name for the document. The name can only contain the following characters:

  • Alphanumeric characters

  • Whitespace characters (no more than one in a row)

  • Hyphens

  • Parentheses

  • Square brackets

This field is vulnerable to prompt injections, because the model might inadvertently interpret it as instructions. Therefore, we recommend that you specify a neutral name.

", + "smithy.api#length": { + "min": 1, + "max": 200 + }, + "smithy.api#required": {} + } + }, + "source": { + "target": "com.amazonaws.bedrockruntime#DocumentSource", + "traits": { + "smithy.api#documentation": "

Contains the content of the document.

", + "smithy.api#required": {} + } + }, + "context": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

Contextual information about how the document should be processed or interpreted by the model when generating citations.

" + } + }, + "citations": { + "target": "com.amazonaws.bedrockruntime#CitationsConfig", + "traits": { + "smithy.api#documentation": "

Configuration settings that control how citations should be generated for this specific document.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A document to include in a message.

" + } + }, + "com.amazonaws.bedrockruntime#DocumentCharLocation": { + "type": "structure", + "members": { + "documentIndex": { + "target": "smithy.api#Integer", + "traits": { + "smithy.api#documentation": "

The index of the document within the array of documents provided in the request.

", + "smithy.api#range": { + "min": 0 + } + } + }, + "start": { + "target": "smithy.api#Integer", + "traits": { + "smithy.api#documentation": "

The starting character position of the cited content within the document.

", + "smithy.api#range": { + "min": 0 + } + } + }, + "end": { + "target": "smithy.api#Integer", + "traits": { + "smithy.api#documentation": "

The ending character position of the cited content within the document.

", + "smithy.api#range": { + "min": 0 + } + } + } + }, + "traits": { + "smithy.api#documentation": "

Specifies a character-level location within a document, providing precise positioning information for cited content using start and end character indices.

" + } + }, + "com.amazonaws.bedrockruntime#DocumentChunkLocation": { + "type": "structure", + "members": { + "documentIndex": { + "target": "smithy.api#Integer", + "traits": { + "smithy.api#documentation": "

The index of the document within the array of documents provided in the request.

", + "smithy.api#range": { + "min": 0 + } + } + }, + "start": { + "target": "smithy.api#Integer", + "traits": { + "smithy.api#documentation": "

The starting chunk identifier or index of the cited content within the document.

", + "smithy.api#range": { + "min": 0 + } + } + }, + "end": { + "target": "smithy.api#Integer", + "traits": { + "smithy.api#documentation": "

The ending chunk identifier or index of the cited content within the document.

", + "smithy.api#range": { + "min": 0 + } + } + } + }, + "traits": { + "smithy.api#documentation": "

Specifies a chunk-level location within a document, providing positioning information for cited content using logical document segments or chunks.

" + } + }, + "com.amazonaws.bedrockruntime#DocumentContentBlock": { + "type": "union", + "members": { + "text": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

The text content of the document.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Contains the actual content of a document that can be processed by the model and potentially cited in the response.

" + } + }, + "com.amazonaws.bedrockruntime#DocumentContentBlocks": { + "type": "list", + "member": { + "target": "com.amazonaws.bedrockruntime#DocumentContentBlock" + } + }, + "com.amazonaws.bedrockruntime#DocumentFormat": { + "type": "enum", + "members": { + "PDF": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "pdf" + } + }, + "CSV": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "csv" + } + }, + "DOC": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "doc" + } + }, + "DOCX": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "docx" + } + }, + "XLS": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "xls" + } + }, + "XLSX": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "xlsx" + } + }, + "HTML": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "html" + } + }, + "TXT": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "txt" + } + }, + "MD": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "md" + } + } + } + }, + "com.amazonaws.bedrockruntime#DocumentPageLocation": { + "type": "structure", + "members": { + "documentIndex": { + "target": "smithy.api#Integer", + "traits": { + "smithy.api#documentation": "

The index of the document within the array of documents provided in the request.

", + "smithy.api#range": { + "min": 0 + } + } + }, + "start": { + "target": "smithy.api#Integer", + "traits": { + "smithy.api#documentation": "

The starting page number of the cited content within the document.

", + "smithy.api#range": { + "min": 0 + } + } + }, + "end": { + "target": "smithy.api#Integer", + "traits": { + "smithy.api#documentation": "

The ending page number of the cited content within the document.

", + "smithy.api#range": { + "min": 0 + } + } + } + }, + "traits": { + "smithy.api#documentation": "

Specifies a page-level location within a document, providing positioning information for cited content using page numbers.

" + } + }, + "com.amazonaws.bedrockruntime#DocumentSource": { + "type": "union", + "members": { + "bytes": { + "target": "smithy.api#Blob", + "traits": { + "smithy.api#documentation": "

The raw bytes for the document. If you use an Amazon Web Services SDK, you don't need to encode the bytes in base64.

", + "smithy.api#length": { + "min": 1 + } + } + }, + "s3Location": { + "target": "com.amazonaws.bedrockruntime#S3Location", + "traits": { + "smithy.api#documentation": "

The location of a document object in an Amazon S3 bucket. To see which models support S3 uploads, see Supported models and features for Converse.

" + } + }, + "text": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

The text content of the document source.

" + } + }, + "content": { + "target": "com.amazonaws.bedrockruntime#DocumentContentBlocks", + "traits": { + "smithy.api#documentation": "

The structured content of the document source, which may include various content blocks such as text, images, or other document elements.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Contains the content of a document.

" + } + }, + "com.amazonaws.bedrockruntime#ErrorBlock": { + "type": "structure", + "members": { + "message": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

A human-readable error message describing what went wrong during content processing.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A block containing error information when content processing fails.

", + "smithy.api#sensitive": {} + } + }, + "com.amazonaws.bedrockruntime#FoundationModelVersionIdentifier": { + "type": "string", + "traits": { + "smithy.api#documentation": "ARN or ID of a Bedrock model", + "smithy.api#length": { + "min": 1, + "max": 256 + }, + "smithy.api#pattern": "^[a-zA-Z_\\.\\-/0-9:]+$" + } + }, + "com.amazonaws.bedrockruntime#GetAsyncInvoke": { + "type": "operation", + "input": { + "target": "com.amazonaws.bedrockruntime#GetAsyncInvokeRequest" + }, + "output": { + "target": "com.amazonaws.bedrockruntime#GetAsyncInvokeResponse" + }, + "errors": [ + { + "target": "com.amazonaws.bedrockruntime#AccessDeniedException" + }, + { + "target": "com.amazonaws.bedrockruntime#InternalServerException" + }, + { + "target": "com.amazonaws.bedrockruntime#ThrottlingException" + }, + { + "target": "com.amazonaws.bedrockruntime#ValidationException" + } + ], + "traits": { + "smithy.api#documentation": "

Retrieve information about an asynchronous invocation.

", + "smithy.api#http": { + "code": 200, + "method": "GET", + "uri": "/async-invoke/{invocationArn}" + }, + "smithy.api#readonly": {} + } + }, + "com.amazonaws.bedrockruntime#GetAsyncInvokeRequest": { + "type": "structure", + "members": { + "invocationArn": { + "target": "com.amazonaws.bedrockruntime#InvocationArn", + "traits": { + "smithy.api#documentation": "

The invocation's ARN.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.bedrockruntime#GetAsyncInvokeResponse": { + "type": "structure", + "members": { + "invocationArn": { + "target": "com.amazonaws.bedrockruntime#InvocationArn", + "traits": { + "smithy.api#documentation": "

The invocation's ARN.

", + "smithy.api#required": {} + } + }, + "modelArn": { + "target": "com.amazonaws.bedrockruntime#AsyncInvokeArn", + "traits": { + "smithy.api#documentation": "

The invocation's model ARN.

", + "smithy.api#required": {} + } + }, + "clientRequestToken": { + "target": "com.amazonaws.bedrockruntime#AsyncInvokeIdempotencyToken", + "traits": { + "smithy.api#documentation": "

The invocation's idempotency token.

" + } + }, + "status": { + "target": "com.amazonaws.bedrockruntime#AsyncInvokeStatus", + "traits": { + "smithy.api#documentation": "

The invocation's status.

", + "smithy.api#required": {} + } + }, + "failureMessage": { + "target": "com.amazonaws.bedrockruntime#AsyncInvokeMessage", + "traits": { + "smithy.api#documentation": "

An error message.

" + } + }, + "submitTime": { + "target": "com.amazonaws.bedrockruntime#Timestamp", + "traits": { + "smithy.api#documentation": "

When the invocation request was submitted.

", + "smithy.api#required": {} + } + }, + "lastModifiedTime": { + "target": "com.amazonaws.bedrockruntime#Timestamp", + "traits": { + "smithy.api#documentation": "

The invocation's last modified time.

" + } + }, + "endTime": { + "target": "com.amazonaws.bedrockruntime#Timestamp", + "traits": { + "smithy.api#documentation": "

When the invocation ended.

" + } + }, + "outputDataConfig": { + "target": "com.amazonaws.bedrockruntime#AsyncInvokeOutputDataConfig", + "traits": { + "smithy.api#documentation": "

Output data settings.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.bedrockruntime#GuardrailAction": { + "type": "enum", + "members": { + "NONE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "NONE" + } + }, + "GUARDRAIL_INTERVENED": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "GUARDRAIL_INTERVENED" + } + } + } + }, + "com.amazonaws.bedrockruntime#GuardrailArn": { + "type": "string", + "traits": { + "smithy.api#length": { + "max": 2048 + }, + "smithy.api#pattern": "^(arn:aws(-[^:]+)?:bedrock:[a-z0-9-]{1,20}:[0-9]{12}:guardrail/[a-z0-9]+)$" + } + }, + "com.amazonaws.bedrockruntime#GuardrailAssessment": { + "type": "structure", + "members": { + "topicPolicy": { + "target": "com.amazonaws.bedrockruntime#GuardrailTopicPolicyAssessment", + "traits": { + "smithy.api#documentation": "

The topic policy.

" + } + }, + "contentPolicy": { + "target": "com.amazonaws.bedrockruntime#GuardrailContentPolicyAssessment", + "traits": { + "smithy.api#documentation": "

The content policy.

" + } + }, + "wordPolicy": { + "target": "com.amazonaws.bedrockruntime#GuardrailWordPolicyAssessment", + "traits": { + "smithy.api#documentation": "

The word policy.

" + } + }, + "sensitiveInformationPolicy": { + "target": "com.amazonaws.bedrockruntime#GuardrailSensitiveInformationPolicyAssessment", + "traits": { + "smithy.api#documentation": "

The sensitive information policy.

" + } + }, + "contextualGroundingPolicy": { + "target": "com.amazonaws.bedrockruntime#GuardrailContextualGroundingPolicyAssessment", + "traits": { + "smithy.api#documentation": "

The contextual grounding policy used for the guardrail assessment.

" + } + }, + "automatedReasoningPolicy": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningPolicyAssessment", + "traits": { + "smithy.api#documentation": "

The automated reasoning policy assessment results, including logical validation findings for the input content.

" + } + }, + "invocationMetrics": { + "target": "com.amazonaws.bedrockruntime#GuardrailInvocationMetrics", + "traits": { + "smithy.api#documentation": "

The invocation metrics for the guardrail assessment.

" + } + }, + "appliedGuardrailDetails": { + "target": "com.amazonaws.bedrockruntime#AppliedGuardrailDetails" + } + }, + "traits": { + "smithy.api#documentation": "

A behavior assessment of the guardrail policies used in a call to the Converse API.

" + } + }, + "com.amazonaws.bedrockruntime#GuardrailAssessmentList": { + "type": "list", + "member": { + "target": "com.amazonaws.bedrockruntime#GuardrailAssessment" + } + }, + "com.amazonaws.bedrockruntime#GuardrailAssessmentListMap": { + "type": "map", + "key": { + "target": "smithy.api#String" + }, + "value": { + "target": "com.amazonaws.bedrockruntime#GuardrailAssessmentList" + } + }, + "com.amazonaws.bedrockruntime#GuardrailAssessmentMap": { + "type": "map", + "key": { + "target": "smithy.api#String" + }, + "value": { + "target": "com.amazonaws.bedrockruntime#GuardrailAssessment" + } + }, + "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningDifferenceScenarioList": { + "type": "list", + "member": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningScenario" + }, + "traits": { + "smithy.api#length": { + "max": 2 + } + } + }, + "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningFinding": { + "type": "union", + "members": { + "valid": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningValidFinding", + "traits": { + "smithy.api#documentation": "

Contains the result when the automated reasoning evaluation determines that the claims in the input are logically valid and definitively true based on the provided premises and policy rules.

" + } + }, + "invalid": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningInvalidFinding", + "traits": { + "smithy.api#documentation": "

Contains the result when the automated reasoning evaluation determines that the claims in the input are logically invalid and contradict the established premises or policy rules.

" + } + }, + "satisfiable": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningSatisfiableFinding", + "traits": { + "smithy.api#documentation": "

Contains the result when the automated reasoning evaluation determines that the claims in the input could be either true or false depending on additional assumptions not provided in the input context.

" + } + }, + "impossible": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningImpossibleFinding", + "traits": { + "smithy.api#documentation": "

Contains the result when the automated reasoning evaluation determines that no valid logical conclusions can be drawn due to contradictions in the premises or policy rules themselves.

" + } + }, + "translationAmbiguous": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningTranslationAmbiguousFinding", + "traits": { + "smithy.api#documentation": "

Contains the result when the automated reasoning evaluation detects that the input has multiple valid logical interpretations, requiring additional context or clarification to proceed with validation.

" + } + }, + "tooComplex": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningTooComplexFinding", + "traits": { + "smithy.api#documentation": "

Contains the result when the automated reasoning evaluation cannot process the input due to its complexity or volume exceeding the system's processing capacity for logical analysis.

" + } + }, + "noTranslations": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningNoTranslationsFinding", + "traits": { + "smithy.api#documentation": "

Contains the result when the automated reasoning evaluation cannot extract any relevant logical information from the input that can be validated against the policy rules.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents a logical validation result from automated reasoning policy evaluation. The finding indicates whether claims in the input are logically valid, invalid, satisfiable, impossible, or have other logical issues.

" + } + }, + "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningFindingList": { + "type": "list", + "member": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningFinding" + } + }, + "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningImpossibleFinding": { + "type": "structure", + "members": { + "translation": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningTranslation", + "traits": { + "smithy.api#documentation": "

The logical translation of the input that this finding evaluates.

" + } + }, + "contradictingRules": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningRuleList", + "traits": { + "smithy.api#documentation": "

The automated reasoning policy rules that contradict the claims and/or premises in the input.

" + } + }, + "logicWarning": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningLogicWarning", + "traits": { + "smithy.api#documentation": "

Indication of a logic issue with the translation without needing to consider the automated reasoning policy rules.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Indicates that no valid claims can be made due to logical contradictions in the premises or rules.

" + } + }, + "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningInputTextReference": { + "type": "structure", + "members": { + "text": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningStatementNaturalLanguageContent", + "traits": { + "smithy.api#documentation": "

The specific text from the original input that this reference points to.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

References a portion of the original input text that corresponds to logical elements.

" + } + }, + "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningInputTextReferenceList": { + "type": "list", + "member": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningInputTextReference" + } + }, + "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningInvalidFinding": { + "type": "structure", + "members": { + "translation": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningTranslation", + "traits": { + "smithy.api#documentation": "

The logical translation of the input that this finding invalidates.

" + } + }, + "contradictingRules": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningRuleList", + "traits": { + "smithy.api#documentation": "

The automated reasoning policy rules that contradict the claims in the input.

" + } + }, + "logicWarning": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningLogicWarning", + "traits": { + "smithy.api#documentation": "

Indication of a logic issue with the translation without needing to consider the automated reasoning policy rules.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Indicates that the claims are logically false and contradictory to the established rules or premises.

" + } + }, + "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningLogicWarning": { + "type": "structure", + "members": { + "type": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningLogicWarningType", + "traits": { + "smithy.api#documentation": "

The category of the detected logical issue, such as statements that are always true or always false.

" + } + }, + "premises": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningStatementList", + "traits": { + "smithy.api#documentation": "

The logical statements that serve as premises under which the claims are validated.

" + } + }, + "claims": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningStatementList", + "traits": { + "smithy.api#documentation": "

The logical statements that are validated while assuming the policy and premises.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Identifies logical issues in the translated statements that exist independent of any policy rules, such as statements that are always true or always false.

" + } + }, + "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningLogicWarningType": { + "type": "enum", + "members": { + "ALWAYS_FALSE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "ALWAYS_FALSE" + } + }, + "ALWAYS_TRUE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "ALWAYS_TRUE" + } + } + } + }, + "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningNoTranslationsFinding": { + "type": "structure", + "members": {}, + "traits": { + "smithy.api#documentation": "

Indicates that no relevant logical information could be extracted from the input for validation.

" + } + }, + "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningPoliciesProcessed": { + "type": "integer" + }, + "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningPolicyAssessment": { + "type": "structure", + "members": { + "findings": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningFindingList", + "traits": { + "smithy.api#documentation": "

List of logical validation results produced by evaluating the input content against automated reasoning policies.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Contains the results of automated reasoning policy evaluation, including logical findings about the validity of claims made in the input content.

" + } + }, + "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningPolicyUnitsProcessed": { + "type": "integer" + }, + "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningPolicyVersionArn": { + "type": "string", + "traits": { + "smithy.api#length": { + "max": 2048 + }, + "smithy.api#pattern": "^arn:aws(-[^:]+)?:bedrock:[a-z0-9-]{1,20}:[0-9]{12}:automated-reasoning-policy/[a-z0-9]{12}(:([1-9][0-9]{0,11}))?$" + } + }, + "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningRule": { + "type": "structure", + "members": { + "identifier": { + "target": "com.amazonaws.bedrockruntime#AutomatedReasoningRuleIdentifier", + "traits": { + "smithy.api#documentation": "

The unique identifier of the automated reasoning rule.

" + } + }, + "policyVersionArn": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningPolicyVersionArn", + "traits": { + "smithy.api#documentation": "

The ARN of the automated reasoning policy version that contains this rule.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

References a specific automated reasoning policy rule that was applied during evaluation.

" + } + }, + "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningRuleList": { + "type": "list", + "member": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningRule" + } + }, + "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningSatisfiableFinding": { + "type": "structure", + "members": { + "translation": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningTranslation", + "traits": { + "smithy.api#documentation": "

The logical translation of the input that this finding evaluates.

" + } + }, + "claimsTrueScenario": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningScenario", + "traits": { + "smithy.api#documentation": "

An example scenario demonstrating how the claims could be logically true.

" + } + }, + "claimsFalseScenario": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningScenario", + "traits": { + "smithy.api#documentation": "

An example scenario demonstrating how the claims could be logically false.

" + } + }, + "logicWarning": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningLogicWarning", + "traits": { + "smithy.api#documentation": "

Indication of a logic issue with the translation without needing to consider the automated reasoning policy rules.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Indicates that the claims could be either true or false depending on additional assumptions not provided in the input.

" + } + }, + "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningScenario": { + "type": "structure", + "members": { + "statements": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningStatementList", + "traits": { + "smithy.api#documentation": "

List of logical assignments and statements that define this scenario.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents a logical scenario where claims can be evaluated as true or false, containing specific logical assignments.

" + } + }, + "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningStatement": { + "type": "structure", + "members": { + "logic": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningStatementLogicContent", + "traits": { + "smithy.api#documentation": "

The formal logical representation of the statement.

" + } + }, + "naturalLanguage": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningStatementNaturalLanguageContent", + "traits": { + "smithy.api#documentation": "

The natural language explanation of the logical statement.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A logical statement that includes both formal logic representation and natural language explanation.

" + } + }, + "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningStatementList": { + "type": "list", + "member": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningStatement" + } + }, + "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningStatementLogicContent": { + "type": "string", + "traits": { + "smithy.api#length": { + "max": 1000 + }, + "smithy.api#sensitive": {} + } + }, + "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningStatementNaturalLanguageContent": { + "type": "string", + "traits": { + "smithy.api#length": { + "max": 1000 + }, + "smithy.api#sensitive": {} + } + }, + "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningTooComplexFinding": { + "type": "structure", + "members": {}, + "traits": { + "smithy.api#documentation": "

Indicates that the input exceeds the processing capacity due to the volume or complexity of the logical information.

" + } + }, + "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningTranslation": { + "type": "structure", + "members": { + "premises": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningStatementList", + "traits": { + "smithy.api#documentation": "

The logical statements that serve as the foundation or assumptions for the claims.

" + } + }, + "claims": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningStatementList", + "traits": { + "smithy.api#documentation": "

The logical statements that are being validated against the premises and policy rules.

" + } + }, + "untranslatedPremises": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningInputTextReferenceList", + "traits": { + "smithy.api#documentation": "

References to portions of the original input text that correspond to the premises but could not be fully translated.

" + } + }, + "untranslatedClaims": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningInputTextReferenceList", + "traits": { + "smithy.api#documentation": "

References to portions of the original input text that correspond to the claims but could not be fully translated.

" + } + }, + "confidence": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningTranslationConfidence", + "traits": { + "smithy.api#documentation": "

A confidence score between 0 and 1 indicating how certain the system is about the logical translation.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Contains the logical translation of natural language input into formal logical statements, including premises, claims, and confidence scores.

" + } + }, + "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningTranslationAmbiguousFinding": { + "type": "structure", + "members": { + "options": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningTranslationOptionList", + "traits": { + "smithy.api#documentation": "

Different logical interpretations that were detected during translation of the input.

" + } + }, + "differenceScenarios": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningDifferenceScenarioList", + "traits": { + "smithy.api#documentation": "

Scenarios showing how the different translation options differ in meaning.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Indicates that the input has multiple valid logical interpretations, requiring additional context or clarification.

" + } + }, + "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningTranslationConfidence": { + "type": "double", + "traits": { + "smithy.api#range": { + "min": 0, + "max": 1 + } + } + }, + "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningTranslationList": { + "type": "list", + "member": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningTranslation" + } + }, + "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningTranslationOption": { + "type": "structure", + "members": { + "translations": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningTranslationList", + "traits": { + "smithy.api#documentation": "

Example translations that provide this possible interpretation of the input.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents one possible logical interpretation of ambiguous input content.

" + } + }, + "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningTranslationOptionList": { + "type": "list", + "member": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningTranslationOption" + }, + "traits": { + "smithy.api#length": { + "max": 2 + } + } + }, + "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningValidFinding": { + "type": "structure", + "members": { + "translation": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningTranslation", + "traits": { + "smithy.api#documentation": "

The logical translation of the input that this finding validates.

" + } + }, + "claimsTrueScenario": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningScenario", + "traits": { + "smithy.api#documentation": "

An example scenario demonstrating how the claims are logically true.

" + } + }, + "supportingRules": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningRuleList", + "traits": { + "smithy.api#documentation": "

The automated reasoning policy rules that support why this result is considered valid.

" + } + }, + "logicWarning": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningLogicWarning", + "traits": { + "smithy.api#documentation": "

Indication of a logic issue with the translation without needing to consider the automated reasoning policy rules.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Indicates that the claims are definitively true and logically implied by the premises, with no possible alternative interpretations.

" + } + }, + "com.amazonaws.bedrockruntime#GuardrailConfiguration": { + "type": "structure", + "members": { + "guardrailIdentifier": { + "target": "com.amazonaws.bedrockruntime#GuardrailIdentifier", + "traits": { + "smithy.api#addedDefault": {}, + "smithy.api#default": "", + "smithy.api#documentation": "

The identifier for the guardrail.

" + } + }, + "guardrailVersion": { + "target": "com.amazonaws.bedrockruntime#GuardrailVersion", + "traits": { + "smithy.api#addedDefault": {}, + "smithy.api#default": "", + "smithy.api#documentation": "

The version of the guardrail.

" + } + }, + "trace": { + "target": "com.amazonaws.bedrockruntime#GuardrailTrace", + "traits": { + "smithy.api#default": "disabled", + "smithy.api#documentation": "

The trace behavior for the guardrail.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Configuration information for a guardrail that you use with the Converse operation.

" + } + }, + "com.amazonaws.bedrockruntime#GuardrailContentBlock": { + "type": "union", + "members": { + "text": { + "target": "com.amazonaws.bedrockruntime#GuardrailTextBlock", + "traits": { + "smithy.api#documentation": "

Text within content block to be evaluated by the guardrail.

" + } + }, + "image": { + "target": "com.amazonaws.bedrockruntime#GuardrailImageBlock", + "traits": { + "smithy.api#documentation": "

Image within guardrail content block to be evaluated by the guardrail.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The content block to be evaluated by the guardrail.

" + } + }, + "com.amazonaws.bedrockruntime#GuardrailContentBlockList": { + "type": "list", + "member": { + "target": "com.amazonaws.bedrockruntime#GuardrailContentBlock" + } + }, + "com.amazonaws.bedrockruntime#GuardrailContentFilter": { + "type": "structure", + "members": { + "type": { + "target": "com.amazonaws.bedrockruntime#GuardrailContentFilterType", + "traits": { + "smithy.api#documentation": "

The guardrail type.

", + "smithy.api#required": {} + } + }, + "confidence": { + "target": "com.amazonaws.bedrockruntime#GuardrailContentFilterConfidence", + "traits": { + "smithy.api#documentation": "

The guardrail confidence.

", + "smithy.api#required": {} + } + }, + "filterStrength": { + "target": "com.amazonaws.bedrockruntime#GuardrailContentFilterStrength", + "traits": { + "smithy.api#documentation": "

The filter strength setting for the guardrail content filter.

" + } + }, + "action": { + "target": "com.amazonaws.bedrockruntime#GuardrailContentPolicyAction", + "traits": { + "smithy.api#documentation": "

The guardrail action.

", + "smithy.api#required": {} + } + }, + "detected": { + "target": "smithy.api#Boolean", + "traits": { + "smithy.api#documentation": "

Indicates whether content that breaches the guardrail configuration is detected.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The content filter for a guardrail.

" + } + }, + "com.amazonaws.bedrockruntime#GuardrailContentFilterConfidence": { + "type": "enum", + "members": { + "NONE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "NONE" + } + }, + "LOW": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "LOW" + } + }, + "MEDIUM": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "MEDIUM" + } + }, + "HIGH": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "HIGH" + } + } + } + }, + "com.amazonaws.bedrockruntime#GuardrailContentFilterList": { + "type": "list", + "member": { + "target": "com.amazonaws.bedrockruntime#GuardrailContentFilter" + } + }, + "com.amazonaws.bedrockruntime#GuardrailContentFilterStrength": { + "type": "enum", + "members": { + "NONE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "NONE" + } + }, + "LOW": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "LOW" + } + }, + "MEDIUM": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "MEDIUM" + } + }, + "HIGH": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "HIGH" + } + } + } + }, + "com.amazonaws.bedrockruntime#GuardrailContentFilterType": { + "type": "enum", + "members": { + "INSULTS": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "INSULTS" + } + }, + "HATE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "HATE" + } + }, + "SEXUAL": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "SEXUAL" + } + }, + "VIOLENCE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "VIOLENCE" + } + }, + "MISCONDUCT": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "MISCONDUCT" + } + }, + "PROMPT_ATTACK": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "PROMPT_ATTACK" + } + } + } + }, + "com.amazonaws.bedrockruntime#GuardrailContentPolicyAction": { + "type": "enum", + "members": { + "BLOCKED": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "BLOCKED" + } + }, + "NONE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "NONE" + } + } + } + }, + "com.amazonaws.bedrockruntime#GuardrailContentPolicyAssessment": { + "type": "structure", + "members": { + "filters": { + "target": "com.amazonaws.bedrockruntime#GuardrailContentFilterList", + "traits": { + "smithy.api#documentation": "

The content policy filters.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

An assessment of a content policy for a guardrail.

" + } + }, + "com.amazonaws.bedrockruntime#GuardrailContentPolicyImageUnitsProcessed": { + "type": "integer" + }, + "com.amazonaws.bedrockruntime#GuardrailContentPolicyUnitsProcessed": { + "type": "integer" + }, + "com.amazonaws.bedrockruntime#GuardrailContentQualifier": { + "type": "enum", + "members": { + "GROUNDING_SOURCE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "grounding_source" + } + }, + "QUERY": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "query" + } + }, + "GUARD_CONTENT": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "guard_content" + } + } + } + }, + "com.amazonaws.bedrockruntime#GuardrailContentQualifierList": { + "type": "list", + "member": { + "target": "com.amazonaws.bedrockruntime#GuardrailContentQualifier" + } + }, + "com.amazonaws.bedrockruntime#GuardrailContentSource": { + "type": "enum", + "members": { + "INPUT": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "INPUT" + } + }, + "OUTPUT": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "OUTPUT" + } + } + } + }, + "com.amazonaws.bedrockruntime#GuardrailContextualGroundingFilter": { + "type": "structure", + "members": { + "type": { + "target": "com.amazonaws.bedrockruntime#GuardrailContextualGroundingFilterType", + "traits": { + "smithy.api#documentation": "

The contextual grounding filter type.

", + "smithy.api#required": {} + } + }, + "threshold": { + "target": "smithy.api#Double", + "traits": { + "smithy.api#documentation": "

The threshold used by contextual grounding filter to determine whether the content is grounded or not.

", + "smithy.api#range": { + "min": 0, + "max": 1 + }, + "smithy.api#required": {} + } + }, + "score": { + "target": "smithy.api#Double", + "traits": { + "smithy.api#documentation": "

The score generated by contextual grounding filter.

", + "smithy.api#range": { + "min": 0, + "max": 1 + }, + "smithy.api#required": {} + } + }, + "action": { + "target": "com.amazonaws.bedrockruntime#GuardrailContextualGroundingPolicyAction", + "traits": { + "smithy.api#documentation": "

The action performed by the guardrails contextual grounding filter.

", + "smithy.api#required": {} + } + }, + "detected": { + "target": "smithy.api#Boolean", + "traits": { + "smithy.api#documentation": "

Indicates whether content that fails the contextual grounding evaluation (grounding or relevance score less than the corresponding threshold) was detected.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The details for the guardrails contextual grounding filter.

" + } + }, + "com.amazonaws.bedrockruntime#GuardrailContextualGroundingFilterType": { + "type": "enum", + "members": { + "GROUNDING": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "GROUNDING" + } + }, + "RELEVANCE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "RELEVANCE" + } + } + } + }, + "com.amazonaws.bedrockruntime#GuardrailContextualGroundingFilters": { + "type": "list", + "member": { + "target": "com.amazonaws.bedrockruntime#GuardrailContextualGroundingFilter" + } + }, + "com.amazonaws.bedrockruntime#GuardrailContextualGroundingPolicyAction": { + "type": "enum", + "members": { + "BLOCKED": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "BLOCKED" + } + }, + "NONE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "NONE" + } + } + } + }, + "com.amazonaws.bedrockruntime#GuardrailContextualGroundingPolicyAssessment": { + "type": "structure", + "members": { + "filters": { + "target": "com.amazonaws.bedrockruntime#GuardrailContextualGroundingFilters", + "traits": { + "smithy.api#documentation": "

The filter details for the guardrails contextual grounding filter.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The policy assessment details for the guardrails contextual grounding filter.

" + } + }, + "com.amazonaws.bedrockruntime#GuardrailContextualGroundingPolicyUnitsProcessed": { + "type": "integer" + }, + "com.amazonaws.bedrockruntime#GuardrailConverseContentBlock": { + "type": "union", + "members": { + "text": { + "target": "com.amazonaws.bedrockruntime#GuardrailConverseTextBlock", + "traits": { + "smithy.api#documentation": "

The text to guard.

" + } + }, + "image": { + "target": "com.amazonaws.bedrockruntime#GuardrailConverseImageBlock", + "traits": { + "smithy.api#documentation": "

Image within converse content block to be evaluated by the guardrail.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A content block for selective guarding with the Converse or ConverseStream API operations.

" + } + }, + "com.amazonaws.bedrockruntime#GuardrailConverseContentQualifier": { + "type": "enum", + "members": { + "GROUNDING_SOURCE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "grounding_source" + } + }, + "QUERY": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "query" + } + }, + "GUARD_CONTENT": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "guard_content" + } + } + } + }, + "com.amazonaws.bedrockruntime#GuardrailConverseContentQualifierList": { + "type": "list", + "member": { + "target": "com.amazonaws.bedrockruntime#GuardrailConverseContentQualifier" + } + }, + "com.amazonaws.bedrockruntime#GuardrailConverseImageBlock": { + "type": "structure", + "members": { + "format": { + "target": "com.amazonaws.bedrockruntime#GuardrailConverseImageFormat", + "traits": { + "smithy.api#documentation": "

The format details for the image type of the guardrail converse image block.

", + "smithy.api#required": {} + } + }, + "source": { + "target": "com.amazonaws.bedrockruntime#GuardrailConverseImageSource", + "traits": { + "smithy.api#documentation": "

The image source (image bytes) of the guardrail converse image block.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

An image block that contains images that you want to assess with a guardrail.

", + "smithy.api#sensitive": {} + } + }, + "com.amazonaws.bedrockruntime#GuardrailConverseImageFormat": { + "type": "enum", + "members": { + "PNG": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "png" + } + }, + "JPEG": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "jpeg" + } + } + } + }, + "com.amazonaws.bedrockruntime#GuardrailConverseImageSource": { + "type": "union", + "members": { + "bytes": { + "target": "smithy.api#Blob", + "traits": { + "smithy.api#documentation": "

The raw image bytes for the image.

", + "smithy.api#length": { + "min": 1 + } + } + } + }, + "traits": { + "smithy.api#documentation": "

The image source (image bytes) of the guardrail converse image source.

", + "smithy.api#sensitive": {} + } + }, + "com.amazonaws.bedrockruntime#GuardrailConverseTextBlock": { + "type": "structure", + "members": { + "text": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

The text that you want to guard.

", + "smithy.api#required": {} + } + }, + "qualifiers": { + "target": "com.amazonaws.bedrockruntime#GuardrailConverseContentQualifierList", + "traits": { + "smithy.api#documentation": "

The qualifier details for the guardrails contextual grounding filter.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A text block that contains text that you want to assess with a guardrail. For more information, see GuardrailConverseContentBlock.

" + } + }, + "com.amazonaws.bedrockruntime#GuardrailCoverage": { + "type": "structure", + "members": { + "textCharacters": { + "target": "com.amazonaws.bedrockruntime#GuardrailTextCharactersCoverage", + "traits": { + "smithy.api#documentation": "

The text characters of the guardrail coverage details.

" + } + }, + "images": { + "target": "com.amazonaws.bedrockruntime#GuardrailImageCoverage", + "traits": { + "smithy.api#documentation": "

The guardrail coverage for images (the number of images that guardrails guarded).

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The action of the guardrail coverage details.

" + } + }, + "com.amazonaws.bedrockruntime#GuardrailCustomWord": { + "type": "structure", + "members": { + "match": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

The match for the custom word.

", + "smithy.api#required": {} + } + }, + "action": { + "target": "com.amazonaws.bedrockruntime#GuardrailWordPolicyAction", + "traits": { + "smithy.api#documentation": "

The action for the custom word.

", + "smithy.api#required": {} + } + }, + "detected": { + "target": "smithy.api#Boolean", + "traits": { + "smithy.api#documentation": "

Indicates whether custom word content that breaches the guardrail configuration is detected.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A custom word configured in a guardrail.

" + } + }, + "com.amazonaws.bedrockruntime#GuardrailCustomWordList": { + "type": "list", + "member": { + "target": "com.amazonaws.bedrockruntime#GuardrailCustomWord" + } + }, + "com.amazonaws.bedrockruntime#GuardrailId": { + "type": "string", + "traits": { + "smithy.api#length": { + "max": 2048 + }, + "smithy.api#pattern": "^([a-z0-9]+)$" + } + }, + "com.amazonaws.bedrockruntime#GuardrailIdentifier": { + "type": "string", + "traits": { + "smithy.api#length": { + "max": 2048 + }, + "smithy.api#pattern": "^(|([a-z0-9]+)|(arn:aws(-[^:]+)?:bedrock:[a-z0-9-]{1,20}:[0-9]{12}:guardrail/[a-z0-9]+))$" + } + }, + "com.amazonaws.bedrockruntime#GuardrailImageBlock": { + "type": "structure", + "members": { + "format": { + "target": "com.amazonaws.bedrockruntime#GuardrailImageFormat", + "traits": { + "smithy.api#documentation": "

The format details for the file type of the image blocked by the guardrail.

", + "smithy.api#required": {} + } + }, + "source": { + "target": "com.amazonaws.bedrockruntime#GuardrailImageSource", + "traits": { + "smithy.api#documentation": "

The image source (image bytes) details of the image blocked by the guardrail.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Contain an image which user wants guarded. This block is accepted by the guardrails independent API.

", + "smithy.api#sensitive": {} + } + }, + "com.amazonaws.bedrockruntime#GuardrailImageCoverage": { + "type": "structure", + "members": { + "guarded": { + "target": "com.amazonaws.bedrockruntime#ImagesGuarded", + "traits": { + "smithy.api#documentation": "

The count (integer) of images guardrails guarded.

" + } + }, + "total": { + "target": "com.amazonaws.bedrockruntime#ImagesTotal", + "traits": { + "smithy.api#documentation": "

Represents the total number of images (integer) that were in the request (guarded and unguarded).

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The details of the guardrail image coverage.

" + } + }, + "com.amazonaws.bedrockruntime#GuardrailImageFormat": { + "type": "enum", + "members": { + "PNG": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "png" + } + }, + "JPEG": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "jpeg" + } + } + } + }, + "com.amazonaws.bedrockruntime#GuardrailImageSource": { + "type": "union", + "members": { + "bytes": { + "target": "smithy.api#Blob", + "traits": { + "smithy.api#documentation": "

The bytes details of the guardrail image source. Object used in independent api.

", + "smithy.api#length": { + "min": 1 + } + } + } + }, + "traits": { + "smithy.api#documentation": "

The image source (image bytes) of the guardrail image source. Object used in independent api.

", + "smithy.api#sensitive": {} + } + }, + "com.amazonaws.bedrockruntime#GuardrailInvocationMetrics": { + "type": "structure", + "members": { + "guardrailProcessingLatency": { + "target": "com.amazonaws.bedrockruntime#GuardrailProcessingLatency", + "traits": { + "smithy.api#documentation": "

The processing latency details for the guardrail invocation metrics.

" + } + }, + "usage": { + "target": "com.amazonaws.bedrockruntime#GuardrailUsage", + "traits": { + "smithy.api#documentation": "

The usage details for the guardrail invocation metrics.

" + } + }, + "guardrailCoverage": { + "target": "com.amazonaws.bedrockruntime#GuardrailCoverage", + "traits": { + "smithy.api#documentation": "

The coverage details for the guardrail invocation metrics.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The invocation metrics for the guardrail.

" + } + }, + "com.amazonaws.bedrockruntime#GuardrailManagedWord": { + "type": "structure", + "members": { + "match": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

The match for the managed word.

", + "smithy.api#required": {} + } + }, + "type": { + "target": "com.amazonaws.bedrockruntime#GuardrailManagedWordType", + "traits": { + "smithy.api#documentation": "

The type for the managed word.

", + "smithy.api#required": {} + } + }, + "action": { + "target": "com.amazonaws.bedrockruntime#GuardrailWordPolicyAction", + "traits": { + "smithy.api#documentation": "

The action for the managed word.

", + "smithy.api#required": {} + } + }, + "detected": { + "target": "smithy.api#Boolean", + "traits": { + "smithy.api#documentation": "

Indicates whether managed word content that breaches the guardrail configuration is detected.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A managed word configured in a guardrail.

" + } + }, + "com.amazonaws.bedrockruntime#GuardrailManagedWordList": { + "type": "list", + "member": { + "target": "com.amazonaws.bedrockruntime#GuardrailManagedWord" + } + }, + "com.amazonaws.bedrockruntime#GuardrailManagedWordType": { + "type": "enum", + "members": { + "PROFANITY": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "PROFANITY" + } + } + } + }, + "com.amazonaws.bedrockruntime#GuardrailOrigin": { + "type": "enum", + "members": { + "REQUEST": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "REQUEST" + } + }, + "ACCOUNT_ENFORCED": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "ACCOUNT_ENFORCED" + } + }, + "ORGANIZATION_ENFORCED": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "ORGANIZATION_ENFORCED" + } + } + } + }, + "com.amazonaws.bedrockruntime#GuardrailOriginList": { + "type": "list", + "member": { + "target": "com.amazonaws.bedrockruntime#GuardrailOrigin" + } + }, + "com.amazonaws.bedrockruntime#GuardrailOutputContent": { + "type": "structure", + "members": { + "text": { + "target": "com.amazonaws.bedrockruntime#GuardrailOutputText", + "traits": { + "smithy.api#documentation": "

The specific text for the output content produced by the guardrail.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The output content produced by the guardrail.

" + } + }, + "com.amazonaws.bedrockruntime#GuardrailOutputContentList": { + "type": "list", + "member": { + "target": "com.amazonaws.bedrockruntime#GuardrailOutputContent" + } + }, + "com.amazonaws.bedrockruntime#GuardrailOutputScope": { + "type": "enum", + "members": { + "INTERVENTIONS": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "INTERVENTIONS" + } + }, + "FULL": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "FULL" + } + } + } + }, + "com.amazonaws.bedrockruntime#GuardrailOutputText": { + "type": "string" + }, + "com.amazonaws.bedrockruntime#GuardrailOwnership": { + "type": "enum", + "members": { + "SELF": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "SELF" + } + }, + "CROSS_ACCOUNT": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "CROSS_ACCOUNT" + } + } + } + }, + "com.amazonaws.bedrockruntime#GuardrailPiiEntityFilter": { + "type": "structure", + "members": { + "match": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

The PII entity filter match.

", + "smithy.api#required": {} + } + }, + "type": { + "target": "com.amazonaws.bedrockruntime#GuardrailPiiEntityType", + "traits": { + "smithy.api#documentation": "

The PII entity filter type.

", + "smithy.api#required": {} + } + }, + "action": { + "target": "com.amazonaws.bedrockruntime#GuardrailSensitiveInformationPolicyAction", + "traits": { + "smithy.api#documentation": "

The PII entity filter action.

", + "smithy.api#required": {} + } + }, + "detected": { + "target": "smithy.api#Boolean", + "traits": { + "smithy.api#documentation": "

Indicates whether personally identifiable information (PII) that breaches the guardrail configuration is detected.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A Personally Identifiable Information (PII) entity configured in a guardrail.

" + } + }, + "com.amazonaws.bedrockruntime#GuardrailPiiEntityFilterList": { + "type": "list", + "member": { + "target": "com.amazonaws.bedrockruntime#GuardrailPiiEntityFilter" + } + }, + "com.amazonaws.bedrockruntime#GuardrailPiiEntityType": { + "type": "enum", + "members": { + "ADDRESS": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "ADDRESS" + } + }, + "AGE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AGE" + } + }, + "AWS_ACCESS_KEY": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS_ACCESS_KEY" + } + }, + "AWS_SECRET_KEY": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS_SECRET_KEY" + } + }, + "CA_HEALTH_NUMBER": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "CA_HEALTH_NUMBER" + } + }, + "CA_SOCIAL_INSURANCE_NUMBER": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "CA_SOCIAL_INSURANCE_NUMBER" + } + }, + "CREDIT_DEBIT_CARD_CVV": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "CREDIT_DEBIT_CARD_CVV" + } + }, + "CREDIT_DEBIT_CARD_EXPIRY": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "CREDIT_DEBIT_CARD_EXPIRY" + } + }, + "CREDIT_DEBIT_CARD_NUMBER": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "CREDIT_DEBIT_CARD_NUMBER" + } + }, + "DRIVER_ID": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "DRIVER_ID" + } + }, + "EMAIL": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "EMAIL" + } + }, + "INTERNATIONAL_BANK_ACCOUNT_NUMBER": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "INTERNATIONAL_BANK_ACCOUNT_NUMBER" + } + }, + "IP_ADDRESS": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "IP_ADDRESS" + } + }, + "LICENSE_PLATE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "LICENSE_PLATE" + } + }, + "MAC_ADDRESS": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "MAC_ADDRESS" + } + }, + "NAME": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "NAME" + } + }, + "PASSWORD": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "PASSWORD" + } + }, + "PHONE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "PHONE" + } + }, + "PIN": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "PIN" + } + }, + "SWIFT_CODE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "SWIFT_CODE" + } + }, + "UK_NATIONAL_HEALTH_SERVICE_NUMBER": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "UK_NATIONAL_HEALTH_SERVICE_NUMBER" + } + }, + "UK_NATIONAL_INSURANCE_NUMBER": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "UK_NATIONAL_INSURANCE_NUMBER" + } + }, + "UK_UNIQUE_TAXPAYER_REFERENCE_NUMBER": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "UK_UNIQUE_TAXPAYER_REFERENCE_NUMBER" + } + }, + "URL": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "URL" + } + }, + "USERNAME": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "USERNAME" + } + }, + "US_BANK_ACCOUNT_NUMBER": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "US_BANK_ACCOUNT_NUMBER" + } + }, + "US_BANK_ROUTING_NUMBER": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "US_BANK_ROUTING_NUMBER" + } + }, + "US_INDIVIDUAL_TAX_IDENTIFICATION_NUMBER": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "US_INDIVIDUAL_TAX_IDENTIFICATION_NUMBER" + } + }, + "US_PASSPORT_NUMBER": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "US_PASSPORT_NUMBER" + } + }, + "US_SOCIAL_SECURITY_NUMBER": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "US_SOCIAL_SECURITY_NUMBER" + } + }, + "VEHICLE_IDENTIFICATION_NUMBER": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "VEHICLE_IDENTIFICATION_NUMBER" + } + } + } + }, + "com.amazonaws.bedrockruntime#GuardrailProcessingLatency": { + "type": "long" + }, + "com.amazonaws.bedrockruntime#GuardrailRegexFilter": { + "type": "structure", + "members": { + "name": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

The regex filter name.

" + } + }, + "match": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

The regesx filter match.

" + } + }, + "regex": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

The regex query.

" + } + }, + "action": { + "target": "com.amazonaws.bedrockruntime#GuardrailSensitiveInformationPolicyAction", + "traits": { + "smithy.api#documentation": "

The region filter action.

", + "smithy.api#required": {} + } + }, + "detected": { + "target": "smithy.api#Boolean", + "traits": { + "smithy.api#documentation": "

Indicates whether custom regex entities that breach the guardrail configuration are detected.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A Regex filter configured in a guardrail.

" + } + }, + "com.amazonaws.bedrockruntime#GuardrailRegexFilterList": { + "type": "list", + "member": { + "target": "com.amazonaws.bedrockruntime#GuardrailRegexFilter" + } + }, + "com.amazonaws.bedrockruntime#GuardrailResource": { + "type": "resource", + "operations": [ + { + "target": "com.amazonaws.bedrockruntime#ApplyGuardrail" + } + ] + }, + "com.amazonaws.bedrockruntime#GuardrailSensitiveInformationPolicyAction": { + "type": "enum", + "members": { + "ANONYMIZED": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "ANONYMIZED" + } + }, + "BLOCKED": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "BLOCKED" + } + }, + "NONE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "NONE" + } + } + } + }, + "com.amazonaws.bedrockruntime#GuardrailSensitiveInformationPolicyAssessment": { + "type": "structure", + "members": { + "piiEntities": { + "target": "com.amazonaws.bedrockruntime#GuardrailPiiEntityFilterList", + "traits": { + "smithy.api#documentation": "

The PII entities in the assessment.

", + "smithy.api#required": {} + } + }, + "regexes": { + "target": "com.amazonaws.bedrockruntime#GuardrailRegexFilterList", + "traits": { + "smithy.api#documentation": "

The regex queries in the assessment.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

The assessment for a Personally Identifiable Information (PII) policy.

" + } + }, + "com.amazonaws.bedrockruntime#GuardrailSensitiveInformationPolicyFreeUnitsProcessed": { + "type": "integer" + }, + "com.amazonaws.bedrockruntime#GuardrailSensitiveInformationPolicyUnitsProcessed": { + "type": "integer" + }, + "com.amazonaws.bedrockruntime#GuardrailStreamConfiguration": { + "type": "structure", + "members": { + "guardrailIdentifier": { + "target": "com.amazonaws.bedrockruntime#GuardrailIdentifier", + "traits": { + "smithy.api#addedDefault": {}, + "smithy.api#default": "", + "smithy.api#documentation": "

The identifier for the guardrail.

" + } + }, + "guardrailVersion": { + "target": "com.amazonaws.bedrockruntime#GuardrailVersion", + "traits": { + "smithy.api#addedDefault": {}, + "smithy.api#default": "", + "smithy.api#documentation": "

The version of the guardrail.

" + } + }, + "trace": { + "target": "com.amazonaws.bedrockruntime#GuardrailTrace", + "traits": { + "smithy.api#default": "disabled", + "smithy.api#documentation": "

The trace behavior for the guardrail.

" + } + }, + "streamProcessingMode": { + "target": "com.amazonaws.bedrockruntime#GuardrailStreamProcessingMode", + "traits": { + "smithy.api#default": "sync", + "smithy.api#documentation": "

The processing mode.

The processing mode. For more information, see Configure streaming response behavior in the Amazon Bedrock User Guide.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Configuration information for a guardrail that you use with the ConverseStream action.

" + } + }, + "com.amazonaws.bedrockruntime#GuardrailStreamProcessingMode": { + "type": "enum", + "members": { + "SYNC": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "sync" + } + }, + "ASYNC": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "async" + } + } + } + }, + "com.amazonaws.bedrockruntime#GuardrailTextBlock": { + "type": "structure", + "members": { + "text": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

The input text details to be evaluated by the guardrail.

", + "smithy.api#required": {} + } + }, + "qualifiers": { + "target": "com.amazonaws.bedrockruntime#GuardrailContentQualifierList", + "traits": { + "smithy.api#documentation": "

The qualifiers describing the text block.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The text block to be evaluated by the guardrail.

" + } + }, + "com.amazonaws.bedrockruntime#GuardrailTextCharactersCoverage": { + "type": "structure", + "members": { + "guarded": { + "target": "com.amazonaws.bedrockruntime#TextCharactersGuarded", + "traits": { + "smithy.api#documentation": "

The text characters that were guarded by the guardrail coverage.

" + } + }, + "total": { + "target": "com.amazonaws.bedrockruntime#TextCharactersTotal", + "traits": { + "smithy.api#documentation": "

The total text characters by the guardrail coverage.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The guardrail coverage for the text characters.

" + } + }, + "com.amazonaws.bedrockruntime#GuardrailTopic": { + "type": "structure", + "members": { + "name": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

The name for the guardrail.

", + "smithy.api#required": {} + } + }, + "type": { + "target": "com.amazonaws.bedrockruntime#GuardrailTopicType", + "traits": { + "smithy.api#documentation": "

The type behavior that the guardrail should perform when the model detects the topic.

", + "smithy.api#required": {} + } + }, + "action": { + "target": "com.amazonaws.bedrockruntime#GuardrailTopicPolicyAction", + "traits": { + "smithy.api#documentation": "

The action the guardrail should take when it intervenes on a topic.

", + "smithy.api#required": {} + } + }, + "detected": { + "target": "smithy.api#Boolean", + "traits": { + "smithy.api#documentation": "

Indicates whether topic content that breaches the guardrail configuration is detected.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Information about a topic guardrail.

" + } + }, + "com.amazonaws.bedrockruntime#GuardrailTopicList": { + "type": "list", + "member": { + "target": "com.amazonaws.bedrockruntime#GuardrailTopic" + } + }, + "com.amazonaws.bedrockruntime#GuardrailTopicPolicyAction": { + "type": "enum", + "members": { + "BLOCKED": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "BLOCKED" + } + }, + "NONE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "NONE" + } + } + } + }, + "com.amazonaws.bedrockruntime#GuardrailTopicPolicyAssessment": { + "type": "structure", + "members": { + "topics": { + "target": "com.amazonaws.bedrockruntime#GuardrailTopicList", + "traits": { + "smithy.api#documentation": "

The topics in the assessment.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

A behavior assessment of a topic policy.

" + } + }, + "com.amazonaws.bedrockruntime#GuardrailTopicPolicyUnitsProcessed": { + "type": "integer" + }, + "com.amazonaws.bedrockruntime#GuardrailTopicType": { + "type": "enum", + "members": { + "DENY": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "DENY" + } + } + } + }, + "com.amazonaws.bedrockruntime#GuardrailTrace": { + "type": "enum", + "members": { + "ENABLED": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "enabled" + } + }, + "DISABLED": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "disabled" + } + }, + "ENABLED_FULL": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "enabled_full" + } + } + } + }, + "com.amazonaws.bedrockruntime#GuardrailTraceAssessment": { + "type": "structure", + "members": { + "modelOutput": { + "target": "com.amazonaws.bedrockruntime#ModelOutputs", + "traits": { + "smithy.api#documentation": "

The output from the model.

" + } + }, + "inputAssessment": { + "target": "com.amazonaws.bedrockruntime#GuardrailAssessmentMap", + "traits": { + "smithy.api#documentation": "

The input assessment.

" + } + }, + "outputAssessments": { + "target": "com.amazonaws.bedrockruntime#GuardrailAssessmentListMap", + "traits": { + "smithy.api#documentation": "

the output assessments.

" + } + }, + "actionReason": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

Provides the reason for the action taken when harmful content is detected.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A Top level guardrail trace object. For more information, see ConverseTrace.

" + } + }, + "com.amazonaws.bedrockruntime#GuardrailUsage": { + "type": "structure", + "members": { + "topicPolicyUnits": { + "target": "com.amazonaws.bedrockruntime#GuardrailTopicPolicyUnitsProcessed", + "traits": { + "smithy.api#documentation": "

The topic policy units processed by the guardrail.

", + "smithy.api#required": {} + } + }, + "contentPolicyUnits": { + "target": "com.amazonaws.bedrockruntime#GuardrailContentPolicyUnitsProcessed", + "traits": { + "smithy.api#documentation": "

The content policy units processed by the guardrail.

", + "smithy.api#required": {} + } + }, + "wordPolicyUnits": { + "target": "com.amazonaws.bedrockruntime#GuardrailWordPolicyUnitsProcessed", + "traits": { + "smithy.api#documentation": "

The word policy units processed by the guardrail.

", + "smithy.api#required": {} + } + }, + "sensitiveInformationPolicyUnits": { + "target": "com.amazonaws.bedrockruntime#GuardrailSensitiveInformationPolicyUnitsProcessed", + "traits": { + "smithy.api#documentation": "

The sensitive information policy units processed by the guardrail.

", + "smithy.api#required": {} + } + }, + "sensitiveInformationPolicyFreeUnits": { + "target": "com.amazonaws.bedrockruntime#GuardrailSensitiveInformationPolicyFreeUnitsProcessed", + "traits": { + "smithy.api#documentation": "

The sensitive information policy free units processed by the guardrail.

", + "smithy.api#required": {} + } + }, + "contextualGroundingPolicyUnits": { + "target": "com.amazonaws.bedrockruntime#GuardrailContextualGroundingPolicyUnitsProcessed", + "traits": { + "smithy.api#documentation": "

The contextual grounding policy units processed by the guardrail.

", + "smithy.api#required": {} + } + }, + "contentPolicyImageUnits": { + "target": "com.amazonaws.bedrockruntime#GuardrailContentPolicyImageUnitsProcessed", + "traits": { + "smithy.api#documentation": "

The content policy image units processed by the guardrail.

" + } + }, + "automatedReasoningPolicyUnits": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningPolicyUnitsProcessed", + "traits": { + "smithy.api#documentation": "

The number of text units processed by the automated reasoning policy.

" + } + }, + "automatedReasoningPolicies": { + "target": "com.amazonaws.bedrockruntime#GuardrailAutomatedReasoningPoliciesProcessed", + "traits": { + "smithy.api#documentation": "

The number of automated reasoning policies that were processed during the guardrail evaluation.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The details on the use of the guardrail.

" + } + }, + "com.amazonaws.bedrockruntime#GuardrailVersion": { + "type": "string", + "traits": { + "smithy.api#pattern": "^(|([1-9][0-9]{0,7})|(DRAFT))$" + } + }, + "com.amazonaws.bedrockruntime#GuardrailWordPolicyAction": { + "type": "enum", + "members": { + "BLOCKED": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "BLOCKED" + } + }, + "NONE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "NONE" + } + } + } + }, + "com.amazonaws.bedrockruntime#GuardrailWordPolicyAssessment": { + "type": "structure", + "members": { + "customWords": { + "target": "com.amazonaws.bedrockruntime#GuardrailCustomWordList", + "traits": { + "smithy.api#documentation": "

Custom words in the assessment.

", + "smithy.api#required": {} + } + }, + "managedWordLists": { + "target": "com.amazonaws.bedrockruntime#GuardrailManagedWordList", + "traits": { + "smithy.api#documentation": "

Managed word lists in the assessment.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

The word policy assessment.

" + } + }, + "com.amazonaws.bedrockruntime#GuardrailWordPolicyUnitsProcessed": { + "type": "integer" + }, + "com.amazonaws.bedrockruntime#ImageBlock": { + "type": "structure", + "members": { + "format": { + "target": "com.amazonaws.bedrockruntime#ImageFormat", + "traits": { + "smithy.api#documentation": "

The format of the image.

", + "smithy.api#required": {} + } + }, + "source": { + "target": "com.amazonaws.bedrockruntime#ImageSource", + "traits": { + "smithy.api#documentation": "

The source for the image.

", + "smithy.api#required": {} + } + }, + "error": { + "target": "com.amazonaws.bedrockruntime#ErrorBlock", + "traits": { + "smithy.api#documentation": "

Error information if the image block could not be processed or contains invalid data.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Image content for a message.

" + } + }, + "com.amazonaws.bedrockruntime#ImageBlockDelta": { + "type": "structure", + "members": { + "source": { + "target": "com.amazonaws.bedrockruntime#ImageSource", + "traits": { + "smithy.api#documentation": "

The incremental image source data for this delta event.

" + } + }, + "error": { + "target": "com.amazonaws.bedrockruntime#ErrorBlock", + "traits": { + "smithy.api#documentation": "

Error information if this image delta could not be processed.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A streaming delta event that contains incremental image data during streaming responses.

" + } + }, + "com.amazonaws.bedrockruntime#ImageBlockStart": { + "type": "structure", + "members": { + "format": { + "target": "com.amazonaws.bedrockruntime#ImageFormat", + "traits": { + "smithy.api#documentation": "

The format of the image data that will be streamed in subsequent delta events.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

The initial event in a streaming image block that indicates the start of image content.

" + } + }, + "com.amazonaws.bedrockruntime#ImageFormat": { + "type": "enum", + "members": { + "PNG": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "png" + } + }, + "JPEG": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "jpeg" + } + }, + "GIF": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "gif" + } + }, + "WEBP": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "webp" + } + } + } + }, + "com.amazonaws.bedrockruntime#ImageSource": { + "type": "union", + "members": { + "bytes": { + "target": "smithy.api#Blob", + "traits": { + "smithy.api#documentation": "

The raw image bytes for the image. If you use an AWS SDK, you don't need to encode the image bytes in base64.

", + "smithy.api#length": { + "min": 1 + } + } + }, + "s3Location": { + "target": "com.amazonaws.bedrockruntime#S3Location", + "traits": { + "smithy.api#documentation": "

The location of an image object in an Amazon S3 bucket. To see which models support S3 uploads, see Supported models and features for Converse.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The source for an image.

", + "smithy.api#sensitive": {} + } + }, + "com.amazonaws.bedrockruntime#ImagesGuarded": { + "type": "integer" + }, + "com.amazonaws.bedrockruntime#ImagesTotal": { + "type": "integer" + }, + "com.amazonaws.bedrockruntime#InferenceConfiguration": { + "type": "structure", + "members": { + "maxTokens": { + "target": "smithy.api#Integer", + "traits": { + "smithy.api#documentation": "

The maximum number of tokens to allow in the generated response. The default value is the maximum allowed value for the model that you are using. For more information, see Inference parameters for foundation models.

", + "smithy.api#range": { + "min": 1 + } + } + }, + "temperature": { + "target": "smithy.api#Float", + "traits": { + "smithy.api#documentation": "

The likelihood of the model selecting higher-probability options while generating a response. A lower value makes the model more likely to choose higher-probability options, while a higher value makes the model more likely to choose lower-probability options.

The default value is the default value for the model that you are using. For more information, see Inference parameters for foundation models.

", + "smithy.api#range": { + "min": 0, + "max": 1 + } + } + }, + "topP": { + "target": "smithy.api#Float", + "traits": { + "smithy.api#documentation": "

The percentage of most-likely candidates that the model considers for the next token. For example, if you choose a value of 0.8 for topP, the model selects from the top 80% of the probability distribution of tokens that could be next in the sequence.

The default value is the default value for the model that you are using. For more information, see Inference parameters for foundation models.

", + "smithy.api#range": { + "min": 0, + "max": 1 + } + } + }, + "stopSequences": { + "target": "com.amazonaws.bedrockruntime#NonEmptyStringList", + "traits": { + "smithy.api#documentation": "

A list of stop sequences. A stop sequence is a sequence of characters that causes the model to stop generating the response.

", + "smithy.api#length": { + "max": 2500 + } + } + } + }, + "traits": { + "smithy.api#documentation": "

Base inference parameters to pass to a model in a call to Converse or ConverseStream. For more information, see Inference parameters for foundation models.

If you need to pass additional parameters that the model supports, use the additionalModelRequestFields request field in the call to Converse or ConverseStream. For more information, see Model parameters.

" + } + }, + "com.amazonaws.bedrockruntime#InferenceResource": { + "type": "resource", + "operations": [ + { + "target": "com.amazonaws.bedrockruntime#Converse" + }, + { + "target": "com.amazonaws.bedrockruntime#ConverseStream" + }, + { + "target": "com.amazonaws.bedrockruntime#InvokeModel" + }, + { + "target": "com.amazonaws.bedrockruntime#InvokeModelWithBidirectionalStream" + }, + { + "target": "com.amazonaws.bedrockruntime#InvokeModelWithResponseStream" + } + ] + }, + "com.amazonaws.bedrockruntime#InternalServerException": { + "type": "structure", + "members": { + "message": { + "target": "com.amazonaws.bedrockruntime#NonBlankString" + } + }, + "traits": { + "smithy.api#documentation": "

An internal server error occurred. For troubleshooting this error, see InternalFailure in the Amazon Bedrock User Guide

", + "smithy.api#error": "server", + "smithy.api#httpError": 500 + } + }, + "com.amazonaws.bedrockruntime#InvocationArn": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 2048 + }, + "smithy.api#pattern": "^arn:aws(-[^:]+)?:bedrock:[a-z0-9-]{1,20}:[0-9]{12}:async-invoke/[a-z0-9]{12}$" + } + }, + "com.amazonaws.bedrockruntime#InvokeModel": { + "type": "operation", + "input": { + "target": "com.amazonaws.bedrockruntime#InvokeModelRequest" + }, + "output": { + "target": "com.amazonaws.bedrockruntime#InvokeModelResponse" + }, + "errors": [ + { + "target": "com.amazonaws.bedrockruntime#AccessDeniedException" + }, + { + "target": "com.amazonaws.bedrockruntime#InternalServerException" + }, + { + "target": "com.amazonaws.bedrockruntime#ModelErrorException" + }, + { + "target": "com.amazonaws.bedrockruntime#ModelNotReadyException" + }, + { + "target": "com.amazonaws.bedrockruntime#ModelTimeoutException" + }, + { + "target": "com.amazonaws.bedrockruntime#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.bedrockruntime#ServiceQuotaExceededException" + }, + { + "target": "com.amazonaws.bedrockruntime#ServiceUnavailableException" + }, + { + "target": "com.amazonaws.bedrockruntime#ThrottlingException" + }, + { + "target": "com.amazonaws.bedrockruntime#ValidationException" + } + ], + "traits": { + "smithy.api#documentation": "

Invokes the specified Amazon Bedrock model to run inference using the prompt and inference parameters provided in the request body. You use model inference to generate text, images, and embeddings.

For example code, see Invoke model code examples in the Amazon Bedrock User Guide.

This operation requires permission for the bedrock:InvokeModel action.

To deny all inference access to resources that you specify in the modelId field, you need to deny access to the bedrock:InvokeModel and bedrock:InvokeModelWithResponseStream actions. Doing this also denies access to the resource through the Converse API actions (Converse and ConverseStream). For more information see Deny access for inference on specific models.

For troubleshooting some of the common errors you might encounter when using the InvokeModel API, see Troubleshooting Amazon Bedrock API Error Codes in the Amazon Bedrock User Guide

", + "smithy.api#http": { + "code": 200, + "method": "POST", + "uri": "/model/{modelId}/invoke" + } + } + }, + "com.amazonaws.bedrockruntime#InvokeModelIdentifier": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 2048 + }, + "smithy.api#pattern": "^(arn:aws(-[^:]+)?:bedrock:[a-z0-9-]{1,20}:(([0-9]{12}:custom-model/[a-z0-9-]{1,63}[.]{1}[a-z0-9-]{1,63}/[a-z0-9]{12})|(:foundation-model/[a-z0-9-]{1,63}[.]{1}[a-z0-9-]{1,63}([.:]?[a-z0-9-]{1,63}))|([0-9]{12}:imported-model/[a-z0-9]{12})|([0-9]{12}:provisioned-model/[a-z0-9]{12})|([0-9]{12}:custom-model-deployment/[a-z0-9]{12})|([0-9]{12}:(inference-profile|application-inference-profile)/[a-zA-Z0-9-:.]+)))|([a-z0-9-]{1,63}[.]{1}[a-z0-9-]{1,63}([.:]?[a-z0-9-]{1,63}))|(([0-9a-zA-Z][_-]?)+)|([a-zA-Z0-9-:.]+)$|(^(arn:aws(-[^:]+)?:bedrock:[a-z0-9-]{1,20}:[0-9]{12}:prompt/[0-9a-zA-Z]{10}(?::[0-9]{1,5})?))$|(^arn:aws:sagemaker:[a-z0-9-]+:[0-9]{12}:endpoint/[a-zA-Z0-9-]+$)|(^arn:aws(-[^:]+)?:bedrock:([0-9a-z-]{1,20}):([0-9]{12}):(default-)?prompt-router/[a-zA-Z0-9-:.]+$)$" + } + }, + "com.amazonaws.bedrockruntime#InvokeModelRequest": { + "type": "structure", + "members": { + "body": { + "target": "com.amazonaws.bedrockruntime#Body", + "traits": { + "smithy.api#documentation": "

The prompt and inference parameters in the format specified in the contentType in the header. You must provide the body in JSON format. To see the format and content of the request and response bodies for different models, refer to Inference parameters. For more information, see Run inference in the Bedrock User Guide.

", + "smithy.api#httpPayload": {} + } + }, + "contentType": { + "target": "com.amazonaws.bedrockruntime#MimeType", + "traits": { + "smithy.api#documentation": "

The MIME type of the input data in the request. You must specify application/json.

", + "smithy.api#httpHeader": "Content-Type" + } + }, + "accept": { + "target": "com.amazonaws.bedrockruntime#MimeType", + "traits": { + "smithy.api#documentation": "

The desired MIME type of the inference body in the response. The default value is application/json.

", + "smithy.api#httpHeader": "Accept" + } + }, + "modelId": { + "target": "com.amazonaws.bedrockruntime#InvokeModelIdentifier", + "traits": { + "smithy.api#documentation": "

The unique identifier of the model to invoke to run inference.

The modelId to provide depends on the type of model or throughput that you use:

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "trace": { + "target": "com.amazonaws.bedrockruntime#Trace", + "traits": { + "smithy.api#documentation": "

Specifies whether to enable or disable the Bedrock trace. If enabled, you can see the full Bedrock trace.

", + "smithy.api#httpHeader": "X-Amzn-Bedrock-Trace" + } + }, + "guardrailIdentifier": { + "target": "com.amazonaws.bedrockruntime#GuardrailIdentifier", + "traits": { + "smithy.api#documentation": "

The unique identifier of the guardrail that you want to use. If you don't provide a value, no guardrail is applied to the invocation.

An error will be thrown in the following situations.

  • You don't provide a guardrail identifier but you specify the amazon-bedrock-guardrailConfig field in the request body.

  • You enable the guardrail but the contentType isn't application/json.

  • You provide a guardrail identifier, but guardrailVersion isn't specified.

", + "smithy.api#httpHeader": "X-Amzn-Bedrock-GuardrailIdentifier" + } + }, + "guardrailVersion": { + "target": "com.amazonaws.bedrockruntime#GuardrailVersion", + "traits": { + "smithy.api#documentation": "

The version number for the guardrail. The value can also be DRAFT.

", + "smithy.api#httpHeader": "X-Amzn-Bedrock-GuardrailVersion" + } + }, + "performanceConfigLatency": { + "target": "com.amazonaws.bedrockruntime#PerformanceConfigLatency", + "traits": { + "smithy.api#default": "standard", + "smithy.api#documentation": "

Model performance settings for the request.

", + "smithy.api#httpHeader": "X-Amzn-Bedrock-PerformanceConfig-Latency" + } + }, + "serviceTier": { + "target": "com.amazonaws.bedrockruntime#ServiceTierType", + "traits": { + "smithy.api#documentation": "

Specifies the processing tier type used for serving the request.

", + "smithy.api#httpHeader": "X-Amzn-Bedrock-Service-Tier" + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.bedrockruntime#InvokeModelResponse": { + "type": "structure", + "members": { + "body": { + "target": "com.amazonaws.bedrockruntime#Body", + "traits": { + "smithy.api#documentation": "

Inference response from the model in the format specified in the contentType header. To see the format and content of the request and response bodies for different models, refer to Inference parameters.

", + "smithy.api#httpPayload": {}, + "smithy.api#required": {} + } + }, + "contentType": { + "target": "com.amazonaws.bedrockruntime#MimeType", + "traits": { + "smithy.api#documentation": "

The MIME type of the inference result.

", + "smithy.api#httpHeader": "Content-Type", + "smithy.api#required": {} + } + }, + "performanceConfigLatency": { + "target": "com.amazonaws.bedrockruntime#PerformanceConfigLatency", + "traits": { + "smithy.api#documentation": "

Model performance settings for the request.

", + "smithy.api#httpHeader": "X-Amzn-Bedrock-PerformanceConfig-Latency" + } + }, + "serviceTier": { + "target": "com.amazonaws.bedrockruntime#ServiceTierType", + "traits": { + "smithy.api#documentation": "

Specifies the processing tier type used for serving the request.

", + "smithy.api#httpHeader": "X-Amzn-Bedrock-Service-Tier" + } + } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.bedrockruntime#InvokeModelTokensRequest": { + "type": "structure", + "members": { + "body": { + "target": "com.amazonaws.bedrockruntime#Body", + "traits": { + "smithy.api#documentation": "

The request body to count tokens for, formatted according to the model's expected input format. To learn about the input format for different models, see Model inference parameters and responses.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

The body of an InvokeModel API request for token counting. This structure mirrors the input format for the InvokeModel operation, allowing you to count tokens for raw text inference requests.

" + } + }, + "com.amazonaws.bedrockruntime#InvokeModelWithBidirectionalStream": { + "type": "operation", + "input": { + "target": "com.amazonaws.bedrockruntime#InvokeModelWithBidirectionalStreamRequest" + }, + "output": { + "target": "com.amazonaws.bedrockruntime#InvokeModelWithBidirectionalStreamResponse" + }, + "errors": [ + { + "target": "com.amazonaws.bedrockruntime#AccessDeniedException" + }, + { + "target": "com.amazonaws.bedrockruntime#InternalServerException" + }, + { + "target": "com.amazonaws.bedrockruntime#ModelErrorException" + }, + { + "target": "com.amazonaws.bedrockruntime#ModelNotReadyException" + }, + { + "target": "com.amazonaws.bedrockruntime#ModelStreamErrorException" + }, + { + "target": "com.amazonaws.bedrockruntime#ModelTimeoutException" + }, + { + "target": "com.amazonaws.bedrockruntime#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.bedrockruntime#ServiceQuotaExceededException" + }, + { + "target": "com.amazonaws.bedrockruntime#ServiceUnavailableException" + }, + { + "target": "com.amazonaws.bedrockruntime#ThrottlingException" + }, + { + "target": "com.amazonaws.bedrockruntime#ValidationException" + } + ], + "traits": { + "smithy.api#documentation": "

Invoke the specified Amazon Bedrock model to run inference using the bidirectional stream. The response is returned in a stream that remains open for 8 minutes. A single session can contain multiple prompts and responses from the model. The prompts to the model are provided as audio files and the model's responses are spoken back to the user and transcribed.

It is possible for users to interrupt the model's response with a new prompt, which will halt the response speech. The model will retain contextual awareness of the conversation while pivoting to respond to the new prompt.

", + "smithy.api#http": { + "code": 200, + "method": "POST", + "uri": "/model/{modelId}/invoke-with-bidirectional-stream" + } + } + }, + "com.amazonaws.bedrockruntime#InvokeModelWithBidirectionalStreamInput": { + "type": "union", + "members": { + "chunk": { + "target": "com.amazonaws.bedrockruntime#BidirectionalInputPayloadPart", + "traits": { + "smithy.api#documentation": "

The audio chunk that is used as input for the invocation step.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Payload content, the speech chunk, for the bidirectional input of the invocation step.

", + "smithy.api#streaming": {} + } + }, + "com.amazonaws.bedrockruntime#InvokeModelWithBidirectionalStreamOutput": { + "type": "union", + "members": { + "chunk": { + "target": "com.amazonaws.bedrockruntime#BidirectionalOutputPayloadPart", + "traits": { + "smithy.api#documentation": "

The speech chunk that was provided as output from the invocation step.

" + } + }, + "internalServerException": { + "target": "com.amazonaws.bedrockruntime#InternalServerException", + "traits": { + "smithy.api#documentation": "

The request encountered an unknown internal error.

" + } + }, + "modelStreamErrorException": { + "target": "com.amazonaws.bedrockruntime#ModelStreamErrorException", + "traits": { + "smithy.api#documentation": "

The request encountered an error with the model stream.

" + } + }, + "validationException": { + "target": "com.amazonaws.bedrockruntime#ValidationException", + "traits": { + "smithy.api#documentation": "

The input fails to satisfy the constraints specified by an Amazon Web Services service.

" + } + }, + "throttlingException": { + "target": "com.amazonaws.bedrockruntime#ThrottlingException", + "traits": { + "smithy.api#documentation": "

The request was denied due to request throttling.

" + } + }, + "modelTimeoutException": { + "target": "com.amazonaws.bedrockruntime#ModelTimeoutException", + "traits": { + "smithy.api#documentation": "

The connection was closed because a request was not received within the timeout period.

" + } + }, + "serviceUnavailableException": { + "target": "com.amazonaws.bedrockruntime#ServiceUnavailableException", + "traits": { + "smithy.api#documentation": "

The request has failed due to a temporary failure of the server.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Output from the bidirectional stream that was used for model invocation.

", + "smithy.api#streaming": {} + } + }, + "com.amazonaws.bedrockruntime#InvokeModelWithBidirectionalStreamRequest": { + "type": "structure", + "members": { + "modelId": { + "target": "com.amazonaws.bedrockruntime#InvokeModelIdentifier", + "traits": { + "smithy.api#documentation": "

The model ID or ARN of the model ID to use. Currently, only amazon.nova-sonic-v1:0 is supported.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "body": { + "target": "com.amazonaws.bedrockruntime#InvokeModelWithBidirectionalStreamInput", + "traits": { + "smithy.api#documentation": "

The prompt and inference parameters in the format specified in the BidirectionalInputPayloadPart in the header. You must provide the body in JSON format. To see the format and content of the request and response bodies for different models, refer to Inference parameters. For more information, see Run inference in the Bedrock User Guide.

", + "smithy.api#httpPayload": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.bedrockruntime#InvokeModelWithBidirectionalStreamResponse": { + "type": "structure", + "members": { + "body": { + "target": "com.amazonaws.bedrockruntime#InvokeModelWithBidirectionalStreamOutput", + "traits": { + "smithy.api#documentation": "

Streaming response from the model in the format specified by the BidirectionalOutputPayloadPart header.

", + "smithy.api#httpPayload": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.bedrockruntime#InvokeModelWithResponseStream": { + "type": "operation", + "input": { + "target": "com.amazonaws.bedrockruntime#InvokeModelWithResponseStreamRequest" + }, + "output": { + "target": "com.amazonaws.bedrockruntime#InvokeModelWithResponseStreamResponse" + }, + "errors": [ + { + "target": "com.amazonaws.bedrockruntime#AccessDeniedException" + }, + { + "target": "com.amazonaws.bedrockruntime#InternalServerException" + }, + { + "target": "com.amazonaws.bedrockruntime#ModelErrorException" + }, + { + "target": "com.amazonaws.bedrockruntime#ModelNotReadyException" + }, + { + "target": "com.amazonaws.bedrockruntime#ModelStreamErrorException" + }, + { + "target": "com.amazonaws.bedrockruntime#ModelTimeoutException" + }, + { + "target": "com.amazonaws.bedrockruntime#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.bedrockruntime#ServiceQuotaExceededException" + }, + { + "target": "com.amazonaws.bedrockruntime#ServiceUnavailableException" + }, + { + "target": "com.amazonaws.bedrockruntime#ThrottlingException" + }, + { + "target": "com.amazonaws.bedrockruntime#ValidationException" + } + ], + "traits": { + "smithy.api#documentation": "

Invoke the specified Amazon Bedrock model to run inference using the prompt and inference parameters provided in the request body. The response is returned in a stream.

To see if a model supports streaming, call GetFoundationModel and check the responseStreamingSupported field in the response.

The CLI doesn't support streaming operations in Amazon Bedrock, including InvokeModelWithResponseStream.

For example code, see Invoke model with streaming code example in the Amazon Bedrock User Guide.

This operation requires permissions to perform the bedrock:InvokeModelWithResponseStream action.

To deny all inference access to resources that you specify in the modelId field, you need to deny access to the bedrock:InvokeModel and bedrock:InvokeModelWithResponseStream actions. Doing this also denies access to the resource through the Converse API actions (Converse and ConverseStream). For more information see Deny access for inference on specific models.

For troubleshooting some of the common errors you might encounter when using the InvokeModelWithResponseStream API, see Troubleshooting Amazon Bedrock API Error Codes in the Amazon Bedrock User Guide

", + "smithy.api#http": { + "code": 200, + "method": "POST", + "uri": "/model/{modelId}/invoke-with-response-stream" + } + } + }, + "com.amazonaws.bedrockruntime#InvokeModelWithResponseStreamRequest": { + "type": "structure", + "members": { + "body": { + "target": "com.amazonaws.bedrockruntime#Body", + "traits": { + "smithy.api#documentation": "

The prompt and inference parameters in the format specified in the contentType in the header. You must provide the body in JSON format. To see the format and content of the request and response bodies for different models, refer to Inference parameters. For more information, see Run inference in the Bedrock User Guide.

", + "smithy.api#httpPayload": {} + } + }, + "contentType": { + "target": "com.amazonaws.bedrockruntime#MimeType", + "traits": { + "smithy.api#documentation": "

The MIME type of the input data in the request. You must specify application/json.

", + "smithy.api#httpHeader": "Content-Type" + } + }, + "accept": { + "target": "com.amazonaws.bedrockruntime#MimeType", + "traits": { + "smithy.api#documentation": "

The desired MIME type of the inference body in the response. The default value is application/json.

", + "smithy.api#httpHeader": "X-Amzn-Bedrock-Accept" + } + }, + "modelId": { + "target": "com.amazonaws.bedrockruntime#InvokeModelIdentifier", + "traits": { + "smithy.api#documentation": "

The unique identifier of the model to invoke to run inference.

The modelId to provide depends on the type of model or throughput that you use:

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "trace": { + "target": "com.amazonaws.bedrockruntime#Trace", + "traits": { + "smithy.api#documentation": "

Specifies whether to enable or disable the Bedrock trace. If enabled, you can see the full Bedrock trace.

", + "smithy.api#httpHeader": "X-Amzn-Bedrock-Trace" + } + }, + "guardrailIdentifier": { + "target": "com.amazonaws.bedrockruntime#GuardrailIdentifier", + "traits": { + "smithy.api#documentation": "

The unique identifier of the guardrail that you want to use. If you don't provide a value, no guardrail is applied to the invocation.

An error is thrown in the following situations.

  • You don't provide a guardrail identifier but you specify the amazon-bedrock-guardrailConfig field in the request body.

  • You enable the guardrail but the contentType isn't application/json.

  • You provide a guardrail identifier, but guardrailVersion isn't specified.

", + "smithy.api#httpHeader": "X-Amzn-Bedrock-GuardrailIdentifier" + } + }, + "guardrailVersion": { + "target": "com.amazonaws.bedrockruntime#GuardrailVersion", + "traits": { + "smithy.api#documentation": "

The version number for the guardrail. The value can also be DRAFT.

", + "smithy.api#httpHeader": "X-Amzn-Bedrock-GuardrailVersion" + } + }, + "performanceConfigLatency": { + "target": "com.amazonaws.bedrockruntime#PerformanceConfigLatency", + "traits": { + "smithy.api#default": "standard", + "smithy.api#documentation": "

Model performance settings for the request.

", + "smithy.api#httpHeader": "X-Amzn-Bedrock-PerformanceConfig-Latency" + } + }, + "serviceTier": { + "target": "com.amazonaws.bedrockruntime#ServiceTierType", + "traits": { + "smithy.api#documentation": "

Specifies the processing tier type used for serving the request.

", + "smithy.api#httpHeader": "X-Amzn-Bedrock-Service-Tier" + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.bedrockruntime#InvokeModelWithResponseStreamResponse": { + "type": "structure", + "members": { + "body": { + "target": "com.amazonaws.bedrockruntime#ResponseStream", + "traits": { + "smithy.api#documentation": "

Inference response from the model in the format specified by the contentType header. To see the format and content of this field for different models, refer to Inference parameters.

", + "smithy.api#httpPayload": {}, + "smithy.api#required": {} + } + }, + "contentType": { + "target": "com.amazonaws.bedrockruntime#MimeType", + "traits": { + "smithy.api#documentation": "

The MIME type of the inference result.

", + "smithy.api#httpHeader": "X-Amzn-Bedrock-Content-Type", + "smithy.api#required": {} + } + }, + "performanceConfigLatency": { + "target": "com.amazonaws.bedrockruntime#PerformanceConfigLatency", + "traits": { + "smithy.api#documentation": "

Model performance settings for the request.

", + "smithy.api#httpHeader": "X-Amzn-Bedrock-PerformanceConfig-Latency" + } + }, + "serviceTier": { + "target": "com.amazonaws.bedrockruntime#ServiceTierType", + "traits": { + "smithy.api#documentation": "

Specifies the processing tier type used for serving the request.

", + "smithy.api#httpHeader": "X-Amzn-Bedrock-Service-Tier" + } + } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.bedrockruntime#InvokedModelId": { + "type": "string", + "traits": { + "smithy.api#pattern": "^(arn:aws(-[^:]+)?:bedrock:[a-z0-9-]{1,20}::foundation-model/[a-z0-9-]{1,63}[.]{1}[a-z0-9-]{1,63}([a-z0-9-]{1,63}[.]){0,2}[a-z0-9-]{1,63}([:][a-z0-9-]{1,63}){0,2})|(arn:aws(|-us-gov|-cn|-iso|-iso-b):bedrock:(|[0-9a-z-]{1,20}):(|[0-9]{12}):inference-profile/[a-zA-Z0-9-:.]+)$" + } + }, + "com.amazonaws.bedrockruntime#JsonSchemaDefinition": { + "type": "structure", + "members": { + "schema": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

The JSON schema to constrain the model's output. For more information, see JSON Schema Reference.

", + "smithy.api#required": {} + } + }, + "name": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

The name of the JSON schema.

" + } + }, + "description": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

A description of the JSON schema.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

JSON schema structured output format options.

" + } + }, + "com.amazonaws.bedrockruntime#KmsKeyId": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 2048 + }, + "smithy.api#pattern": "^arn:aws(-[^:]+)?:kms:[a-zA-Z0-9-]*:[0-9]{12}:((key/[a-zA-Z0-9-]{36})|(alias/[a-zA-Z0-9-_/]+))$" + } + }, + "com.amazonaws.bedrockruntime#ListAsyncInvokes": { + "type": "operation", + "input": { + "target": "com.amazonaws.bedrockruntime#ListAsyncInvokesRequest" + }, + "output": { + "target": "com.amazonaws.bedrockruntime#ListAsyncInvokesResponse" + }, + "errors": [ + { + "target": "com.amazonaws.bedrockruntime#AccessDeniedException" + }, + { + "target": "com.amazonaws.bedrockruntime#InternalServerException" + }, + { + "target": "com.amazonaws.bedrockruntime#ThrottlingException" + }, + { + "target": "com.amazonaws.bedrockruntime#ValidationException" + } + ], + "traits": { + "smithy.api#documentation": "

Lists asynchronous invocations.

", + "smithy.api#http": { + "code": 200, + "method": "GET", + "uri": "/async-invoke" + }, + "smithy.api#paginated": { + "inputToken": "nextToken", + "outputToken": "nextToken", + "pageSize": "maxResults", + "items": "asyncInvokeSummaries" + }, + "smithy.api#readonly": {} + } + }, + "com.amazonaws.bedrockruntime#ListAsyncInvokesRequest": { + "type": "structure", + "members": { + "submitTimeAfter": { + "target": "com.amazonaws.bedrockruntime#Timestamp", + "traits": { + "smithy.api#documentation": "

Include invocations submitted after this time.

", + "smithy.api#httpQuery": "submitTimeAfter" + } + }, + "submitTimeBefore": { + "target": "com.amazonaws.bedrockruntime#Timestamp", + "traits": { + "smithy.api#documentation": "

Include invocations submitted before this time.

", + "smithy.api#httpQuery": "submitTimeBefore" + } + }, + "statusEquals": { + "target": "com.amazonaws.bedrockruntime#AsyncInvokeStatus", + "traits": { + "smithy.api#documentation": "

Filter invocations by status.

", + "smithy.api#httpQuery": "statusEquals" + } + }, + "maxResults": { + "target": "com.amazonaws.bedrockruntime#MaxResults", + "traits": { + "smithy.api#documentation": "

The maximum number of invocations to return in one page of results.

", + "smithy.api#httpQuery": "maxResults" + } + }, + "nextToken": { + "target": "com.amazonaws.bedrockruntime#PaginationToken", + "traits": { + "smithy.api#documentation": "

Specify the pagination token from a previous request to retrieve the next page of results.

", + "smithy.api#httpQuery": "nextToken" + } + }, + "sortBy": { + "target": "com.amazonaws.bedrockruntime#SortAsyncInvocationBy", + "traits": { + "smithy.api#default": "SubmissionTime", + "smithy.api#documentation": "

How to sort the response.

", + "smithy.api#httpQuery": "sortBy" + } + }, + "sortOrder": { + "target": "com.amazonaws.bedrockruntime#SortOrder", + "traits": { + "smithy.api#default": "Descending", + "smithy.api#documentation": "

The sorting order for the response.

", + "smithy.api#httpQuery": "sortOrder" + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.bedrockruntime#ListAsyncInvokesResponse": { + "type": "structure", + "members": { + "nextToken": { + "target": "com.amazonaws.bedrockruntime#PaginationToken", + "traits": { + "smithy.api#documentation": "

Specify the pagination token from a previous request to retrieve the next page of results.

" + } + }, + "asyncInvokeSummaries": { + "target": "com.amazonaws.bedrockruntime#AsyncInvokeSummaries", + "traits": { + "smithy.api#documentation": "

A list of invocation summaries.

" + } + } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.bedrockruntime#MaxResults": { + "type": "integer", + "traits": { + "smithy.api#range": { + "min": 1, + "max": 1000 + } + } + }, + "com.amazonaws.bedrockruntime#Message": { + "type": "structure", + "members": { + "role": { + "target": "com.amazonaws.bedrockruntime#ConversationRole", + "traits": { + "smithy.api#documentation": "

The role that the message plays in the message.

", + "smithy.api#required": {} + } + }, + "content": { + "target": "com.amazonaws.bedrockruntime#ContentBlocks", + "traits": { + "smithy.api#documentation": "

The message content. Note the following restrictions:

  • You can include up to 20 images. Each image's size, height, and width must be no more than 3.75 MB, 8000 px, and 8000 px, respectively.

  • You can include up to five documents. Each document's size must be no more than 4.5 MB.

  • If you include a ContentBlock with a document field in the array, you must also include a ContentBlock with a text field.

  • You can only include images and documents if the role is user.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

A message input, or returned from, a call to Converse or ConverseStream.

" + } + }, + "com.amazonaws.bedrockruntime#MessageStartEvent": { + "type": "structure", + "members": { + "role": { + "target": "com.amazonaws.bedrockruntime#ConversationRole", + "traits": { + "smithy.api#documentation": "

The role for the message.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

The start of a message.

" + } + }, + "com.amazonaws.bedrockruntime#MessageStopEvent": { + "type": "structure", + "members": { + "stopReason": { + "target": "com.amazonaws.bedrockruntime#StopReason", + "traits": { + "smithy.api#documentation": "

The reason why the model stopped generating output.

", + "smithy.api#required": {} + } + }, + "additionalModelResponseFields": { + "target": "smithy.api#Document", + "traits": { + "smithy.api#documentation": "

The additional model response fields.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The stop event for a message.

" + } + }, + "com.amazonaws.bedrockruntime#Messages": { + "type": "list", + "member": { + "target": "com.amazonaws.bedrockruntime#Message" + } + }, + "com.amazonaws.bedrockruntime#MimeType": { + "type": "string" + }, + "com.amazonaws.bedrockruntime#ModelErrorException": { + "type": "structure", + "members": { + "message": { + "target": "com.amazonaws.bedrockruntime#NonBlankString" + }, + "originalStatusCode": { + "target": "com.amazonaws.bedrockruntime#StatusCode", + "traits": { + "smithy.api#documentation": "

The original status code.

" + } + }, + "resourceName": { + "target": "com.amazonaws.bedrockruntime#NonBlankString", + "traits": { + "smithy.api#documentation": "

The resource name.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The request failed due to an error while processing the model.

", + "smithy.api#error": "client", + "smithy.api#httpError": 424 + } + }, + "com.amazonaws.bedrockruntime#ModelInputPayload": { + "type": "document", + "traits": { + "smithy.api#sensitive": {} + } + }, + "com.amazonaws.bedrockruntime#ModelNotReadyException": { + "type": "structure", + "members": { + "message": { + "target": "com.amazonaws.bedrockruntime#NonBlankString" + } + }, + "traits": { + "smithy.api#documentation": "

The model specified in the request is not ready to serve inference requests. The AWS SDK will automatically retry the operation up to 5 times. For information about configuring automatic retries, see Retry behavior in the AWS SDKs and Tools reference guide.

", + "smithy.api#error": "client", + "smithy.api#httpError": 429, + "smithy.api#retryable": {} + } + }, + "com.amazonaws.bedrockruntime#ModelOutputs": { + "type": "list", + "member": { + "target": "com.amazonaws.bedrockruntime#GuardrailOutputText" + } + }, + "com.amazonaws.bedrockruntime#ModelStreamErrorException": { + "type": "structure", + "members": { + "message": { + "target": "com.amazonaws.bedrockruntime#NonBlankString" + }, + "originalStatusCode": { + "target": "com.amazonaws.bedrockruntime#StatusCode", + "traits": { + "smithy.api#documentation": "

The original status code.

" + } + }, + "originalMessage": { + "target": "com.amazonaws.bedrockruntime#NonBlankString", + "traits": { + "smithy.api#documentation": "

The original message.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

An error occurred while streaming the response. Retry your request.

", + "smithy.api#error": "client", + "smithy.api#httpError": 424 + } + }, + "com.amazonaws.bedrockruntime#ModelTimeoutException": { + "type": "structure", + "members": { + "message": { + "target": "com.amazonaws.bedrockruntime#NonBlankString" + } + }, + "traits": { + "smithy.api#documentation": "

The request took too long to process. Processing time exceeded the model timeout length.

", + "smithy.api#error": "client", + "smithy.api#httpError": 408 + } + }, + "com.amazonaws.bedrockruntime#NonBlankString": { + "type": "string", + "traits": { + "smithy.api#pattern": "^[\\s\\S]*$" + } + }, + "com.amazonaws.bedrockruntime#NonEmptyString": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1 + } + } + }, + "com.amazonaws.bedrockruntime#NonEmptyStringList": { + "type": "list", + "member": { + "target": "com.amazonaws.bedrockruntime#NonEmptyString" + } + }, + "com.amazonaws.bedrockruntime#NonNegativeInteger": { + "type": "integer", + "traits": { + "smithy.api#range": { + "min": 0 + } + } + }, + "com.amazonaws.bedrockruntime#OutputConfig": { + "type": "structure", + "members": { + "textFormat": { + "target": "com.amazonaws.bedrockruntime#OutputFormat", + "traits": { + "smithy.api#documentation": "

Structured output parameters to control the model's text response.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Output configuration for a model response in a call to Converse or ConverseStream.

" + } + }, + "com.amazonaws.bedrockruntime#OutputFormat": { + "type": "structure", + "members": { + "type": { + "target": "com.amazonaws.bedrockruntime#OutputFormatType", + "traits": { + "smithy.api#documentation": "

The type of structured output format.

", + "smithy.api#required": {} + } + }, + "structure": { + "target": "com.amazonaws.bedrockruntime#OutputFormatStructure", + "traits": { + "smithy.api#documentation": "

The structure that the model's output must adhere to.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Structured output parameters to control the model's response.

" + } + }, + "com.amazonaws.bedrockruntime#OutputFormatStructure": { + "type": "union", + "members": { + "jsonSchema": { + "target": "com.amazonaws.bedrockruntime#JsonSchemaDefinition", + "traits": { + "smithy.api#documentation": "

A JSON schema structure that the model's output must adhere to.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The structure that the model's output must adhere to.

", + "smithy.api#sensitive": {} + } + }, + "com.amazonaws.bedrockruntime#OutputFormatType": { + "type": "enum", + "members": { + "JSON_SCHEMA": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "json_schema" + } + } + }, + "traits": { + "smithy.api#documentation": "

The type of structured output format. Available options are: json_schema.

" + } + }, + "com.amazonaws.bedrockruntime#PaginationToken": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 2048 + }, + "smithy.api#pattern": "^\\S*$" + } + }, + "com.amazonaws.bedrockruntime#PartBody": { + "type": "blob", + "traits": { + "smithy.api#length": { + "max": 1000000 + }, + "smithy.api#sensitive": {} + } + }, + "com.amazonaws.bedrockruntime#PayloadPart": { + "type": "structure", + "members": { + "bytes": { + "target": "com.amazonaws.bedrockruntime#PartBody", + "traits": { + "smithy.api#documentation": "

Base64-encoded bytes of payload data.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Payload content included in the response.

", + "smithy.api#sensitive": {} + } + }, + "com.amazonaws.bedrockruntime#PerformanceConfigLatency": { + "type": "enum", + "members": { + "STANDARD": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "standard" + } + }, + "OPTIMIZED": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "optimized" + } + } + } + }, + "com.amazonaws.bedrockruntime#PerformanceConfiguration": { + "type": "structure", + "members": { + "latency": { + "target": "com.amazonaws.bedrockruntime#PerformanceConfigLatency", + "traits": { + "smithy.api#default": "standard", + "smithy.api#documentation": "

To use a latency-optimized version of the model, set to optimized.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Performance settings for a model.

" + } + }, + "com.amazonaws.bedrockruntime#PromptRouterTrace": { + "type": "structure", + "members": { + "invokedModelId": { + "target": "com.amazonaws.bedrockruntime#InvokedModelId", + "traits": { + "smithy.api#documentation": "

The ID of the invoked model.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A prompt router trace.

" + } + }, + "com.amazonaws.bedrockruntime#PromptVariableMap": { + "type": "map", + "key": { + "target": "smithy.api#String" + }, + "value": { + "target": "com.amazonaws.bedrockruntime#PromptVariableValues" + }, + "traits": { + "smithy.api#sensitive": {} + } + }, + "com.amazonaws.bedrockruntime#PromptVariableValues": { + "type": "union", + "members": { + "text": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

The text value that the variable maps to.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Contains a map of variables in a prompt from Prompt management to an object containing the values to fill in for them when running model invocation. For more information, see How Prompt management works.

" + } + }, + "com.amazonaws.bedrockruntime#ReasoningContentBlock": { + "type": "union", + "members": { + "reasoningText": { + "target": "com.amazonaws.bedrockruntime#ReasoningTextBlock", + "traits": { + "smithy.api#documentation": "

The reasoning that the model used to return the output.

" + } + }, + "redactedContent": { + "target": "smithy.api#Blob", + "traits": { + "smithy.api#documentation": "

The content in the reasoning that was encrypted by the model provider for safety reasons. The encryption doesn't affect the quality of responses.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Contains content regarding the reasoning that is carried out by the model with respect to the content in the content block. Reasoning refers to a Chain of Thought (CoT) that the model generates to enhance the accuracy of its final response.

", + "smithy.api#sensitive": {} + } + }, + "com.amazonaws.bedrockruntime#ReasoningContentBlockDelta": { + "type": "union", + "members": { + "text": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

The reasoning that the model used to return the output.

" + } + }, + "redactedContent": { + "target": "smithy.api#Blob", + "traits": { + "smithy.api#documentation": "

The content in the reasoning that was encrypted by the model provider for safety reasons. The encryption doesn't affect the quality of responses.

" + } + }, + "signature": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

A token that verifies that the reasoning text was generated by the model. If you pass a reasoning block back to the API in a multi-turn conversation, include the text and its signature unmodified.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Contains content regarding the reasoning that is carried out by the model with respect to the content in the content block. Reasoning refers to a Chain of Thought (CoT) that the model generates to enhance the accuracy of its final response.

", + "smithy.api#sensitive": {} + } + }, + "com.amazonaws.bedrockruntime#ReasoningTextBlock": { + "type": "structure", + "members": { + "text": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

The reasoning that the model used to return the output.

", + "smithy.api#required": {} + } + }, + "signature": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

A token that verifies that the reasoning text was generated by the model. If you pass a reasoning block back to the API in a multi-turn conversation, include the text and its signature unmodified.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Contains the reasoning that the model used to return the output.

", + "smithy.api#sensitive": {} + } + }, + "com.amazonaws.bedrockruntime#RequestMetadata": { + "type": "map", + "key": { + "target": "smithy.api#String", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 256 + }, + "smithy.api#pattern": "^[a-zA-Z0-9\\s:_@$#=/+,-.]{1,256}$" + } + }, + "value": { + "target": "smithy.api#String", + "traits": { + "smithy.api#length": { + "min": 0, + "max": 256 + }, + "smithy.api#pattern": "^[a-zA-Z0-9\\s:_@$#=/+,-.]{0,256}$" + } + }, + "traits": { + "smithy.api#length": { + "min": 1, + "max": 16 + }, + "smithy.api#sensitive": {} + } + }, + "com.amazonaws.bedrockruntime#ResourceNotFoundException": { + "type": "structure", + "members": { + "message": { + "target": "com.amazonaws.bedrockruntime#NonBlankString" + } + }, + "traits": { + "smithy.api#documentation": "

The specified resource ARN was not found. For troubleshooting this error, see ResourceNotFound in the Amazon Bedrock User Guide

", + "smithy.api#error": "client", + "smithy.api#httpError": 404 + } + }, + "com.amazonaws.bedrockruntime#ResponseStream": { + "type": "union", + "members": { + "chunk": { + "target": "com.amazonaws.bedrockruntime#PayloadPart", + "traits": { + "smithy.api#documentation": "

Content included in the response.

" + } + }, + "internalServerException": { + "target": "com.amazonaws.bedrockruntime#InternalServerException", + "traits": { + "smithy.api#documentation": "

An internal server error occurred. Retry your request.

" + } + }, + "modelStreamErrorException": { + "target": "com.amazonaws.bedrockruntime#ModelStreamErrorException", + "traits": { + "smithy.api#documentation": "

An error occurred while streaming the response. Retry your request.

" + } + }, + "validationException": { + "target": "com.amazonaws.bedrockruntime#ValidationException", + "traits": { + "smithy.api#documentation": "

Input validation failed. Check your request parameters and retry the request.

" + } + }, + "throttlingException": { + "target": "com.amazonaws.bedrockruntime#ThrottlingException", + "traits": { + "smithy.api#documentation": "

Your request was throttled because of service-wide limitations. Resubmit your request later or in a different region. You can also purchase Provisioned Throughput to increase the rate or number of tokens you can process.

" + } + }, + "modelTimeoutException": { + "target": "com.amazonaws.bedrockruntime#ModelTimeoutException", + "traits": { + "smithy.api#documentation": "

The request took too long to process. Processing time exceeded the model timeout length.

" + } + }, + "serviceUnavailableException": { + "target": "com.amazonaws.bedrockruntime#ServiceUnavailableException", + "traits": { + "smithy.api#documentation": "

The service isn't available. Try again later.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Definition of content in the response stream.

", + "smithy.api#streaming": {} + } + }, + "com.amazonaws.bedrockruntime#S3Location": { + "type": "structure", + "members": { + "uri": { + "target": "com.amazonaws.bedrockruntime#S3Uri", + "traits": { + "smithy.api#documentation": "

An object URI starting with s3://.

", + "smithy.api#required": {} + } + }, + "bucketOwner": { + "target": "com.amazonaws.bedrockruntime#AccountId", + "traits": { + "smithy.api#documentation": "

If the bucket belongs to another AWS account, specify that account's ID.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A storage location in an Amazon S3 bucket.

" + } + }, + "com.amazonaws.bedrockruntime#S3Uri": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 1024 + }, + "smithy.api#pattern": "^s3://[a-z0-9][\\.\\-a-z0-9]{1,61}[a-z0-9](/.*)?$" + } + }, + "com.amazonaws.bedrockruntime#SearchResultBlock": { + "type": "structure", + "members": { + "source": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

The source URL or identifier for the content.

", + "smithy.api#required": {} + } + }, + "title": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

A descriptive title for the search result.

", + "smithy.api#required": {} + } + }, + "content": { + "target": "com.amazonaws.bedrockruntime#SearchResultContentBlocks", + "traits": { + "smithy.api#documentation": "

An array of search result content block.

", + "smithy.api#required": {} + } + }, + "citations": { + "target": "com.amazonaws.bedrockruntime#CitationsConfig", + "traits": { + "smithy.api#documentation": "

Configuration setting for citations

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A search result block that enables natural citations with proper source attribution for retrieved content.

This field is only supported by Anthropic Claude Opus 4.1, Opus 4, Sonnet 4.5, Sonnet 4, Sonnet 3.7, and 3.5 Haiku models.

" + } + }, + "com.amazonaws.bedrockruntime#SearchResultContentBlock": { + "type": "structure", + "members": { + "text": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

The actual text content

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

A block within a search result that contains the content.

" + } + }, + "com.amazonaws.bedrockruntime#SearchResultContentBlocks": { + "type": "list", + "member": { + "target": "com.amazonaws.bedrockruntime#SearchResultContentBlock" + } + }, + "com.amazonaws.bedrockruntime#SearchResultLocation": { + "type": "structure", + "members": { + "searchResultIndex": { + "target": "smithy.api#Integer", + "traits": { + "smithy.api#documentation": "

The index of the search result content block where the cited content is found.

", + "smithy.api#range": { + "min": 0 + } + } + }, + "start": { + "target": "smithy.api#Integer", + "traits": { + "smithy.api#documentation": "

The starting position in the content array where the cited content begins.

", + "smithy.api#range": { + "min": 0 + } + } + }, + "end": { + "target": "smithy.api#Integer", + "traits": { + "smithy.api#documentation": "

The ending position in the content array where the cited content ends.

", + "smithy.api#range": { + "min": 0 + } + } + } + }, + "traits": { + "smithy.api#documentation": "

Specifies a search result location within the content array, providing positioning information for cited content using search result index and block positions.

" + } + }, + "com.amazonaws.bedrockruntime#ServiceQuotaExceededException": { + "type": "structure", + "members": { + "message": { + "target": "com.amazonaws.bedrockruntime#NonBlankString" + } + }, + "traits": { + "smithy.api#documentation": "

Your request exceeds the service quota for your account. You can view your quotas at Viewing service quotas. You can resubmit your request later.

", + "smithy.api#error": "client", + "smithy.api#httpError": 400 + } + }, + "com.amazonaws.bedrockruntime#ServiceTier": { + "type": "structure", + "members": { + "type": { + "target": "com.amazonaws.bedrockruntime#ServiceTierType", + "traits": { + "smithy.api#documentation": "

Specifies the processing tier type used for serving the request.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Specifies the processing tier configuration used for serving the request.

" + } + }, + "com.amazonaws.bedrockruntime#ServiceTierType": { + "type": "enum", + "members": { + "PRIORITY": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "priority" + } + }, + "DEFAULT": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "default" + } + }, + "FLEX": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "flex" + } + }, + "RESERVED": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "reserved" + } + } + } + }, + "com.amazonaws.bedrockruntime#ServiceUnavailableException": { + "type": "structure", + "members": { + "message": { + "target": "com.amazonaws.bedrockruntime#NonBlankString" + } + }, + "traits": { + "smithy.api#documentation": "

The service isn't currently available. For troubleshooting this error, see ServiceUnavailable in the Amazon Bedrock User Guide

", + "smithy.api#error": "server", + "smithy.api#httpError": 503 + } + }, + "com.amazonaws.bedrockruntime#SortAsyncInvocationBy": { + "type": "enum", + "members": { + "SUBMISSION_TIME": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "SubmissionTime" + } + } + } + }, + "com.amazonaws.bedrockruntime#SortOrder": { + "type": "enum", + "members": { + "ASCENDING": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "Ascending" + } + }, + "DESCENDING": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "Descending" + } + } + } + }, + "com.amazonaws.bedrockruntime#SpecificToolChoice": { + "type": "structure", + "members": { + "name": { + "target": "com.amazonaws.bedrockruntime#ToolName", + "traits": { + "smithy.api#documentation": "

The name of the tool that the model must request.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

The model must request a specific tool. For example, {\"tool\" : {\"name\" : \"Your tool name\"}}. For more information, see Call a tool with the Converse API in the Amazon Bedrock User Guide

This field is only supported by Anthropic Claude 3 models.

" + } + }, + "com.amazonaws.bedrockruntime#StartAsyncInvoke": { + "type": "operation", + "input": { + "target": "com.amazonaws.bedrockruntime#StartAsyncInvokeRequest" + }, + "output": { + "target": "com.amazonaws.bedrockruntime#StartAsyncInvokeResponse" + }, + "errors": [ + { + "target": "com.amazonaws.bedrockruntime#AccessDeniedException" + }, + { + "target": "com.amazonaws.bedrockruntime#ConflictException" + }, + { + "target": "com.amazonaws.bedrockruntime#InternalServerException" + }, + { + "target": "com.amazonaws.bedrockruntime#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.bedrockruntime#ServiceQuotaExceededException" + }, + { + "target": "com.amazonaws.bedrockruntime#ServiceUnavailableException" + }, + { + "target": "com.amazonaws.bedrockruntime#ThrottlingException" + }, + { + "target": "com.amazonaws.bedrockruntime#ValidationException" + } + ], + "traits": { + "smithy.api#documentation": "

Starts an asynchronous invocation.

This operation requires permission for the bedrock:InvokeModel action.

To deny all inference access to resources that you specify in the modelId field, you need to deny access to the bedrock:InvokeModel and bedrock:InvokeModelWithResponseStream actions. Doing this also denies access to the resource through the Converse API actions (Converse and ConverseStream). For more information see Deny access for inference on specific models.

", + "smithy.api#http": { + "code": 200, + "method": "POST", + "uri": "/async-invoke" + }, + "smithy.api#idempotent": {} + } + }, + "com.amazonaws.bedrockruntime#StartAsyncInvokeRequest": { + "type": "structure", + "members": { + "clientRequestToken": { + "target": "com.amazonaws.bedrockruntime#AsyncInvokeIdempotencyToken", + "traits": { + "smithy.api#documentation": "

Specify idempotency token to ensure that requests are not duplicated.

", + "smithy.api#idempotencyToken": {} + } + }, + "modelId": { + "target": "com.amazonaws.bedrockruntime#AsyncInvokeIdentifier", + "traits": { + "smithy.api#documentation": "

The model to invoke.

", + "smithy.api#required": {} + } + }, + "modelInput": { + "target": "com.amazonaws.bedrockruntime#ModelInputPayload", + "traits": { + "smithy.api#documentation": "

Input to send to the model.

", + "smithy.api#required": {} + } + }, + "outputDataConfig": { + "target": "com.amazonaws.bedrockruntime#AsyncInvokeOutputDataConfig", + "traits": { + "smithy.api#documentation": "

Where to store the output.

", + "smithy.api#required": {} + } + }, + "tags": { + "target": "com.amazonaws.bedrockruntime#TagList", + "traits": { + "smithy.api#documentation": "

Tags to apply to the invocation.

" + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.bedrockruntime#StartAsyncInvokeResponse": { + "type": "structure", + "members": { + "invocationArn": { + "target": "com.amazonaws.bedrockruntime#InvocationArn", + "traits": { + "smithy.api#documentation": "

The ARN of the invocation.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.bedrockruntime#StatusCode": { + "type": "integer", + "traits": { + "smithy.api#range": { + "min": 100, + "max": 599 + } + } + }, + "com.amazonaws.bedrockruntime#StopReason": { + "type": "enum", + "members": { + "END_TURN": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "end_turn" + } + }, + "TOOL_USE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "tool_use" + } + }, + "MAX_TOKENS": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "max_tokens" + } + }, + "STOP_SEQUENCE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "stop_sequence" + } + }, + "GUARDRAIL_INTERVENED": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "guardrail_intervened" + } + }, + "CONTENT_FILTERED": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "content_filtered" + } + }, + "MALFORMED_MODEL_OUTPUT": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "malformed_model_output" + } + }, + "MALFORMED_TOOL_USE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "malformed_tool_use" + } + }, + "MODEL_CONTEXT_WINDOW_EXCEEDED": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "model_context_window_exceeded" + } + } + } + }, + "com.amazonaws.bedrockruntime#SystemContentBlock": { + "type": "union", + "members": { + "text": { + "target": "com.amazonaws.bedrockruntime#NonEmptyString", + "traits": { + "smithy.api#documentation": "

A system prompt for the model.

" + } + }, + "guardContent": { + "target": "com.amazonaws.bedrockruntime#GuardrailConverseContentBlock", + "traits": { + "smithy.api#documentation": "

A content block to assess with the guardrail. Use with the Converse or ConverseStream API operations.

For more information, see Use a guardrail with the Converse API in the Amazon Bedrock User Guide.

" + } + }, + "cachePoint": { + "target": "com.amazonaws.bedrockruntime#CachePointBlock", + "traits": { + "smithy.api#documentation": "

CachePoint to include in the system prompt.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Contains configurations for instructions to provide the model for how to handle input. To learn more, see Using the Converse API.

" + } + }, + "com.amazonaws.bedrockruntime#SystemContentBlocks": { + "type": "list", + "member": { + "target": "com.amazonaws.bedrockruntime#SystemContentBlock" + } + }, + "com.amazonaws.bedrockruntime#SystemTool": { + "type": "structure", + "members": { + "name": { + "target": "com.amazonaws.bedrockruntime#ToolName", + "traits": { + "smithy.api#documentation": "

The name of the system-defined tool that you want to call.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Specifies a system-defined tool for the model to use. System-defined tools are tools that are created and provided by the model provider.

" + } + }, + "com.amazonaws.bedrockruntime#Tag": { + "type": "structure", + "members": { + "key": { + "target": "com.amazonaws.bedrockruntime#TagKey", + "traits": { + "smithy.api#documentation": "

The tag's key.

", + "smithy.api#required": {} + } + }, + "value": { + "target": "com.amazonaws.bedrockruntime#TagValue", + "traits": { + "smithy.api#documentation": "

The tag's value.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

A tag.

" + } + }, + "com.amazonaws.bedrockruntime#TagKey": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 128 + }, + "smithy.api#pattern": "^[a-zA-Z0-9\\s._:/=+@-]*$" + } + }, + "com.amazonaws.bedrockruntime#TagList": { + "type": "list", + "member": { + "target": "com.amazonaws.bedrockruntime#Tag" + }, + "traits": { + "smithy.api#length": { + "min": 0, + "max": 200 + } + } + }, + "com.amazonaws.bedrockruntime#TagValue": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 0, + "max": 256 + }, + "smithy.api#pattern": "^[a-zA-Z0-9\\s._:/=+@-]*$" + } + }, + "com.amazonaws.bedrockruntime#TextCharactersGuarded": { + "type": "integer" + }, + "com.amazonaws.bedrockruntime#TextCharactersTotal": { + "type": "integer" + }, + "com.amazonaws.bedrockruntime#ThrottlingException": { + "type": "structure", + "members": { + "message": { + "target": "com.amazonaws.bedrockruntime#NonBlankString" + } + }, + "traits": { + "smithy.api#documentation": "

Your request was denied due to exceeding the account quotas for Amazon Bedrock. For troubleshooting this error, see ThrottlingException in the Amazon Bedrock User Guide

", + "smithy.api#error": "client", + "smithy.api#httpError": 429 + } + }, + "com.amazonaws.bedrockruntime#Timestamp": { + "type": "timestamp", + "traits": { + "smithy.api#timestampFormat": "date-time" + } + }, + "com.amazonaws.bedrockruntime#TokenUsage": { + "type": "structure", + "members": { + "inputTokens": { + "target": "smithy.api#Integer", + "traits": { + "smithy.api#documentation": "

The number of tokens sent in the request to the model.

", + "smithy.api#range": { + "min": 0 + }, + "smithy.api#required": {} + } + }, + "outputTokens": { + "target": "smithy.api#Integer", + "traits": { + "smithy.api#documentation": "

The number of tokens that the model generated for the request.

", + "smithy.api#range": { + "min": 0 + }, + "smithy.api#required": {} + } + }, + "totalTokens": { + "target": "smithy.api#Integer", + "traits": { + "smithy.api#documentation": "

The total of input tokens and tokens generated by the model.

", + "smithy.api#range": { + "min": 0 + }, + "smithy.api#required": {} + } + }, + "cacheReadInputTokens": { + "target": "smithy.api#Integer", + "traits": { + "smithy.api#documentation": "

The number of input tokens read from the cache for the request.

", + "smithy.api#range": { + "min": 0 + } + } + }, + "cacheWriteInputTokens": { + "target": "smithy.api#Integer", + "traits": { + "smithy.api#documentation": "

The number of input tokens written to the cache for the request.

", + "smithy.api#range": { + "min": 0 + } + } + }, + "cacheDetails": { + "target": "com.amazonaws.bedrockruntime#CacheDetailsList", + "traits": { + "smithy.api#documentation": "

Detailed breakdown of cache writes by TTL. Empty if no cache creation occurred. Sorted by TTL duration (1h before 5m).

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The tokens used in a message API inference call.

" + } + }, + "com.amazonaws.bedrockruntime#TokenizerResource": { + "type": "resource", + "operations": [ + { + "target": "com.amazonaws.bedrockruntime#CountTokens" + } + ] + }, + "com.amazonaws.bedrockruntime#Tool": { + "type": "union", + "members": { + "toolSpec": { + "target": "com.amazonaws.bedrockruntime#ToolSpecification", + "traits": { + "smithy.api#documentation": "

The specfication for the tool.

" + } + }, + "systemTool": { + "target": "com.amazonaws.bedrockruntime#SystemTool", + "traits": { + "smithy.api#documentation": "

Specifies the system-defined tool that you want use.

" + } + }, + "cachePoint": { + "target": "com.amazonaws.bedrockruntime#CachePointBlock", + "traits": { + "smithy.api#documentation": "

CachePoint to include in the tool configuration.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Information about a tool that you can use with the Converse API. For more information, see Call a tool with the Converse API in the Amazon Bedrock User Guide.

" + } + }, + "com.amazonaws.bedrockruntime#ToolChoice": { + "type": "union", + "members": { + "auto": { + "target": "com.amazonaws.bedrockruntime#AutoToolChoice", + "traits": { + "smithy.api#documentation": "

(Default). The Model automatically decides if a tool should be called or whether to generate text instead.

" + } + }, + "any": { + "target": "com.amazonaws.bedrockruntime#AnyToolChoice", + "traits": { + "smithy.api#documentation": "

The model must request at least one tool (no text is generated).

" + } + }, + "tool": { + "target": "com.amazonaws.bedrockruntime#SpecificToolChoice", + "traits": { + "smithy.api#documentation": "

The Model must request the specified tool. Only supported by Anthropic Claude 3 and Amazon Nova models.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Determines which tools the model should request in a call to Converse or ConverseStream. For more information, see Call a tool with the Converse API in the Amazon Bedrock User Guide.

" + } + }, + "com.amazonaws.bedrockruntime#ToolConfiguration": { + "type": "structure", + "members": { + "tools": { + "target": "com.amazonaws.bedrockruntime#Tools", + "traits": { + "smithy.api#documentation": "

An array of tools that you want to pass to a model.

", + "smithy.api#length": { + "min": 1 + }, + "smithy.api#required": {} + } + }, + "toolChoice": { + "target": "com.amazonaws.bedrockruntime#ToolChoice", + "traits": { + "smithy.api#documentation": "

If supported by model, forces the model to request a tool.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Configuration information for the tools that you pass to a model. For more information, see Tool use (function calling) in the Amazon Bedrock User Guide.

" + } + }, + "com.amazonaws.bedrockruntime#ToolInputSchema": { + "type": "union", + "members": { + "json": { + "target": "smithy.api#Document", + "traits": { + "smithy.api#documentation": "

The JSON schema for the tool. For more information, see JSON Schema Reference.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The schema for the tool. The top level schema type must be object. For more information, see Call a tool with the Converse API in the Amazon Bedrock User Guide.

" + } + }, + "com.amazonaws.bedrockruntime#ToolName": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 64 + }, + "smithy.api#pattern": "^[a-zA-Z0-9_-]+$" + } + }, + "com.amazonaws.bedrockruntime#ToolResultBlock": { + "type": "structure", + "members": { + "toolUseId": { + "target": "com.amazonaws.bedrockruntime#ToolUseId", + "traits": { + "smithy.api#documentation": "

The ID of the tool request that this is the result for.

", + "smithy.api#required": {} + } + }, + "content": { + "target": "com.amazonaws.bedrockruntime#ToolResultContentBlocks", + "traits": { + "smithy.api#documentation": "

The content for tool result content block.

", + "smithy.api#required": {} + } + }, + "status": { + "target": "com.amazonaws.bedrockruntime#ToolResultStatus", + "traits": { + "smithy.api#documentation": "

The status for the tool result content block.

This field is only supported by Amazon Nova and Anthropic Claude 3 and 4 models.

" + } + }, + "type": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

The type for the tool result content block.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A tool result block that contains the results for a tool request that the model previously made. For more information, see Call a tool with the Converse API in the Amazon Bedrock User Guide.

" + } + }, + "com.amazonaws.bedrockruntime#ToolResultBlockDelta": { + "type": "union", + "members": { + "text": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

The reasoning the model used to return the output.

" + } + }, + "json": { + "target": "smithy.api#Document", + "traits": { + "smithy.api#documentation": "

The JSON schema for the tool result content block. see JSON Schema Reference.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Contains incremental updates to tool results information during streaming responses. This allows clients to build up tool results data progressively as the response is generated.

" + } + }, + "com.amazonaws.bedrockruntime#ToolResultBlockStart": { + "type": "structure", + "members": { + "toolUseId": { + "target": "com.amazonaws.bedrockruntime#ToolUseId", + "traits": { + "smithy.api#documentation": "

The ID of the tool that was used to generate this tool result block.

", + "smithy.api#required": {} + } + }, + "type": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

The type for the tool that was used to generate this tool result block.

" + } + }, + "status": { + "target": "com.amazonaws.bedrockruntime#ToolResultStatus", + "traits": { + "smithy.api#documentation": "

The status of the tool result block.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The start of a tool result block. For more information, see Call a tool with the Converse API in the Amazon Bedrock User Guide.

" + } + }, + "com.amazonaws.bedrockruntime#ToolResultBlocksDelta": { + "type": "list", + "member": { + "target": "com.amazonaws.bedrockruntime#ToolResultBlockDelta" + } + }, + "com.amazonaws.bedrockruntime#ToolResultContentBlock": { + "type": "union", + "members": { + "json": { + "target": "smithy.api#Document", + "traits": { + "smithy.api#documentation": "

A tool result that is JSON format data.

" + } + }, + "text": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

A tool result that is text.

" + } + }, + "image": { + "target": "com.amazonaws.bedrockruntime#ImageBlock", + "traits": { + "smithy.api#documentation": "

A tool result that is an image.

This field is only supported by Amazon Nova and Anthropic Claude 3 and 4 models.

" + } + }, + "document": { + "target": "com.amazonaws.bedrockruntime#DocumentBlock", + "traits": { + "smithy.api#documentation": "

A tool result that is a document.

" + } + }, + "video": { + "target": "com.amazonaws.bedrockruntime#VideoBlock", + "traits": { + "smithy.api#documentation": "

A tool result that is video.

" + } + }, + "searchResult": { + "target": "com.amazonaws.bedrockruntime#SearchResultBlock", + "traits": { + "smithy.api#documentation": "

A tool result that is a search result.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The tool result content block. For more information, see Call a tool with the Converse API in the Amazon Bedrock User Guide.

" + } + }, + "com.amazonaws.bedrockruntime#ToolResultContentBlocks": { + "type": "list", + "member": { + "target": "com.amazonaws.bedrockruntime#ToolResultContentBlock" + } + }, + "com.amazonaws.bedrockruntime#ToolResultStatus": { + "type": "enum", + "members": { + "SUCCESS": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "success" + } + }, + "ERROR": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "error" + } + } + } + }, + "com.amazonaws.bedrockruntime#ToolSpecification": { + "type": "structure", + "members": { + "name": { + "target": "com.amazonaws.bedrockruntime#ToolName", + "traits": { + "smithy.api#documentation": "

The name for the tool.

", + "smithy.api#required": {} + } + }, + "description": { + "target": "com.amazonaws.bedrockruntime#NonEmptyString", + "traits": { + "smithy.api#documentation": "

The description for the tool.

" + } + }, + "inputSchema": { + "target": "com.amazonaws.bedrockruntime#ToolInputSchema", + "traits": { + "smithy.api#documentation": "

The input schema for the tool in JSON format.

", + "smithy.api#required": {} + } + }, + "strict": { + "target": "smithy.api#Boolean", + "traits": { + "smithy.api#documentation": "

Flag to enable structured output enforcement on a tool usage response.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The specification for the tool. For more information, see Call a tool with the Converse API in the Amazon Bedrock User Guide.

" + } + }, + "com.amazonaws.bedrockruntime#ToolUseBlock": { + "type": "structure", + "members": { + "toolUseId": { + "target": "com.amazonaws.bedrockruntime#ToolUseId", + "traits": { + "smithy.api#documentation": "

The ID for the tool request.

", + "smithy.api#required": {} + } + }, + "name": { + "target": "com.amazonaws.bedrockruntime#ToolName", + "traits": { + "smithy.api#documentation": "

The name of the tool that the model wants to use.

", + "smithy.api#required": {} + } + }, + "input": { + "target": "smithy.api#Document", + "traits": { + "smithy.api#documentation": "

The input to pass to the tool.

", + "smithy.api#required": {} + } + }, + "type": { + "target": "com.amazonaws.bedrockruntime#ToolUseType", + "traits": { + "smithy.api#documentation": "

The type for the tool request.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A tool use content block. Contains information about a tool that the model is requesting be run., The model uses the result from the tool to generate a response. For more information, see Call a tool with the Converse API in the Amazon Bedrock User Guide.

" + } + }, + "com.amazonaws.bedrockruntime#ToolUseBlockDelta": { + "type": "structure", + "members": { + "input": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

The input for a requested tool.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

The delta for a tool use block.

" + } + }, + "com.amazonaws.bedrockruntime#ToolUseBlockStart": { + "type": "structure", + "members": { + "toolUseId": { + "target": "com.amazonaws.bedrockruntime#ToolUseId", + "traits": { + "smithy.api#documentation": "

The ID for the tool request.

", + "smithy.api#required": {} + } + }, + "name": { + "target": "com.amazonaws.bedrockruntime#ToolName", + "traits": { + "smithy.api#documentation": "

The name of the tool that the model is requesting to use.

", + "smithy.api#required": {} + } + }, + "type": { + "target": "com.amazonaws.bedrockruntime#ToolUseType", + "traits": { + "smithy.api#documentation": "

The type for the tool request.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The start of a tool use block. For more information, see Call a tool with the Converse API in the Amazon Bedrock User Guide.

" + } + }, + "com.amazonaws.bedrockruntime#ToolUseId": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 64 + }, + "smithy.api#pattern": "^[a-zA-Z0-9_-]+$" + } + }, + "com.amazonaws.bedrockruntime#ToolUseType": { + "type": "enum", + "members": { + "SERVER_TOOL_USE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "server_tool_use" + } + } + } + }, + "com.amazonaws.bedrockruntime#Tools": { + "type": "list", + "member": { + "target": "com.amazonaws.bedrockruntime#Tool" + } + }, + "com.amazonaws.bedrockruntime#Trace": { + "type": "enum", + "members": { + "ENABLED": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "ENABLED" + } + }, + "DISABLED": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "DISABLED" + } + }, + "ENABLED_FULL": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "ENABLED_FULL" + } + } + } + }, + "com.amazonaws.bedrockruntime#ValidationException": { + "type": "structure", + "members": { + "message": { + "target": "com.amazonaws.bedrockruntime#NonBlankString" + } + }, + "traits": { + "smithy.api#documentation": "

The input fails to satisfy the constraints specified by Amazon Bedrock. For troubleshooting this error, see ValidationError in the Amazon Bedrock User Guide

", + "smithy.api#error": "client", + "smithy.api#httpError": 400 + } + }, + "com.amazonaws.bedrockruntime#VideoBlock": { + "type": "structure", + "members": { + "format": { + "target": "com.amazonaws.bedrockruntime#VideoFormat", + "traits": { + "smithy.api#documentation": "

The block's format.

", + "smithy.api#required": {} + } + }, + "source": { + "target": "com.amazonaws.bedrockruntime#VideoSource", + "traits": { + "smithy.api#documentation": "

The block's source.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

A video block.

" + } + }, + "com.amazonaws.bedrockruntime#VideoFormat": { + "type": "enum", + "members": { + "MKV": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "mkv" + } + }, + "MOV": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "mov" + } + }, + "MP4": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "mp4" + } + }, + "WEBM": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "webm" + } + }, + "FLV": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "flv" + } + }, + "MPEG": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "mpeg" + } + }, + "MPG": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "mpg" + } + }, + "WMV": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "wmv" + } + }, + "THREE_GP": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "three_gp" + } + } + } + }, + "com.amazonaws.bedrockruntime#VideoSource": { + "type": "union", + "members": { + "bytes": { + "target": "smithy.api#Blob", + "traits": { + "smithy.api#documentation": "

Video content encoded in base64.

", + "smithy.api#length": { + "min": 1 + } + } + }, + "s3Location": { + "target": "com.amazonaws.bedrockruntime#S3Location", + "traits": { + "smithy.api#documentation": "

The location of a video object in an Amazon S3 bucket. To see which models support S3 uploads, see Supported models and features for Converse.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A video source. You can upload a smaller video as a base64-encoded string as long as the encoded file is less than 25MB. You can also transfer videos up to 1GB in size from an S3 bucket.

" + } + }, + "com.amazonaws.bedrockruntime#WebLocation": { + "type": "structure", + "members": { + "url": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

The URL that was cited when performing a web search.

" + } + }, + "domain": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "

The domain that was cited when performing a web search.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Provides the URL and domain information for the website that was cited when performing a web search.

" + } + } + } +} \ No newline at end of file diff --git a/strands-agentcore-smithy/infrastructure/template.yaml b/strands-agentcore-smithy/infrastructure/template.yaml new file mode 100644 index 0000000000..8192d20cf0 --- /dev/null +++ b/strands-agentcore-smithy/infrastructure/template.yaml @@ -0,0 +1,219 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + AgentCore Smithy Bedrock Runtime - Serverless AI agent exposing Bedrock Runtime + operations through AWS Bedrock AgentCore Gateway using a Smithy model target. + Built and deployed with AWS SAM (Makefile build, no Docker required). + +Parameters: + GatewayName: + Type: String + Default: smithy-bedrock-gateway + Description: Name for the AgentCore Gateway + +Resources: + # ============================================================ + # Cognito User Pool and Client + # ============================================================ + CognitoUserPool: + Type: AWS::Cognito::UserPool + Properties: + UserPoolName: !Sub '${AWS::StackName}-UserPool' + AutoVerifiedAttributes: + - email + Schema: + - Name: email + Required: true + Mutable: true + + CognitoUserPoolClient: + Type: AWS::Cognito::UserPoolClient + Properties: + ClientName: !Sub '${AWS::StackName}-Client' + UserPoolId: !Ref CognitoUserPool + ExplicitAuthFlows: + - USER_PASSWORD_AUTH + GenerateSecret: false + + # ============================================================ + # Gateway Execution Role + # ============================================================ + GatewayExecutionRole: + Type: AWS::IAM::Role + Properties: + RoleName: !Sub '${AWS::StackName}-GatewayExecutionRole' + AssumeRolePolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Principal: + Service: bedrock-agentcore.amazonaws.com + Action: sts:AssumeRole + Policies: + - PolicyName: AgentCoreAccess + PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - bedrock-agentcore:* + Resource: + - !Sub 'arn:aws:bedrock-agentcore:${AWS::Region}:${AWS::AccountId}:token-vault/default' + - !Sub 'arn:aws:bedrock-agentcore:${AWS::Region}:${AWS::AccountId}:token-vault/default/apikeycredentialprovider/*' + - !Sub 'arn:aws:bedrock-agentcore:${AWS::Region}:${AWS::AccountId}:workload-identity-directory/default' + - !Sub 'arn:aws:bedrock-agentcore:${AWS::Region}:${AWS::AccountId}:workload-identity-directory/default/workload-identity/${GatewayName}-*' + - PolicyName: BedrockRuntimeAccess + PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - bedrock:InvokeModel + - bedrock:InvokeModelWithResponseStream + - bedrock:Converse + - bedrock:ConverseStream + Resource: + - !Sub 'arn:aws:bedrock:${AWS::Region}::foundation-model/*' + - !Sub 'arn:aws:bedrock:${AWS::Region}:${AWS::AccountId}:inference-profile/*' + - '*' + - PolicyName: SmithyModelS3Access + PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - s3:GetObject + Resource: + - !Sub 'arn:aws:s3:::${AWS::StackName}-smithy-models/*' + + # ============================================================ + # AgentCore Gateway + # ============================================================ + AgentCoreGateway: + Type: AWS::BedrockAgentCore::Gateway + Properties: + Name: !Ref GatewayName + ProtocolType: MCP + ExceptionLevel: DEBUG + AuthorizerType: CUSTOM_JWT + AuthorizerConfiguration: + CustomJWTAuthorizer: + DiscoveryUrl: !Sub 'https://cognito-idp.${AWS::Region}.amazonaws.com/${CognitoUserPool}/.well-known/openid-configuration' + AllowedAudience: + - !Ref CognitoUserPoolClient + RoleArn: !GetAtt GatewayExecutionRole.Arn + + # ============================================================ + # AgentCore Gateway Target (Smithy Model) + # ============================================================ + SmithyTarget: + Type: AWS::BedrockAgentCore::GatewayTarget + Properties: + GatewayIdentifier: !Ref AgentCoreGateway + Name: bedrock-runtime-target + CredentialProviderConfigurations: + - CredentialProviderType: GATEWAY_IAM_ROLE + TargetConfiguration: + Mcp: + SmithyModel: + S3: + Uri: !Sub 's3://${AWS::StackName}-smithy-models/bedrock-runtime-2023-09-30.json' + + # ============================================================ + # Agent Lambda Execution Role + # ============================================================ + AgentLambdaRole: + Type: AWS::IAM::Role + Properties: + RoleName: !Sub '${AWS::StackName}-AgentLambdaRole' + AssumeRolePolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Principal: + Service: lambda.amazonaws.com + Action: sts:AssumeRole + Policies: + - PolicyName: BedrockAccess + PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - bedrock:InvokeModel + - bedrock:InvokeModelWithResponseStream + - bedrock:Converse + - bedrock:ConverseStream + Resource: '*' + - PolicyName: AgentCoreAccess + PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - bedrock-agentcore:InvokeGateway + Resource: + - !Sub 'arn:aws:bedrock-agentcore:${AWS::Region}:${AWS::AccountId}:gateway/${AgentCoreGateway}' + - PolicyName: CloudWatchLogsAccess + PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - logs:CreateLogGroup + - logs:CreateLogStream + - logs:PutLogEvents + Resource: '*' + + # ============================================================ + # Agent Lambda Function (SAM Serverless Function, Makefile build) + # ============================================================ + AgentLambdaFunction: + Type: AWS::Serverless::Function + Metadata: + BuildMethod: makefile + Properties: + FunctionName: !Sub '${AWS::StackName}-AgentFunction' + CodeUri: ../ + Handler: src.agent.handler.lambda_handler + Runtime: python3.12 + Architectures: + - x86_64 + Role: !GetAtt AgentLambdaRole.Arn + Timeout: 120 + MemorySize: 1024 + Environment: + Variables: + GATEWAY_ID: !Ref AgentCoreGateway + GATEWAY_URL: !GetAtt AgentCoreGateway.GatewayUrl + COGNITO_USER_POOL_ID: !Ref CognitoUserPool + COGNITO_CLIENT_ID: !Ref CognitoUserPoolClient + AWS_REGION_NAME: !Ref AWS::Region + +# ============================================================ +# Stack Outputs +# ============================================================ +Outputs: + GatewayId: + Description: AgentCore Gateway ID + Value: !Ref AgentCoreGateway + Export: + Name: !Sub '${AWS::StackName}-GatewayId' + + CognitoUserPoolId: + Description: Cognito User Pool ID + Value: !Ref CognitoUserPool + Export: + Name: !Sub '${AWS::StackName}-CognitoUserPoolId' + + CognitoClientId: + Description: Cognito User Pool Client ID + Value: !Ref CognitoUserPoolClient + Export: + Name: !Sub '${AWS::StackName}-CognitoClientId' + + LambdaFunctionName: + Description: Agent Lambda Function Name + Value: !Ref AgentLambdaFunction + Export: + Name: !Sub '${AWS::StackName}-LambdaFunctionName' diff --git a/strands-agentcore-smithy/requirements.txt b/strands-agentcore-smithy/requirements.txt new file mode 100644 index 0000000000..d4a19fa8e1 --- /dev/null +++ b/strands-agentcore-smithy/requirements.txt @@ -0,0 +1,4 @@ +strands-agents +mcp +requests +PyJWT[crypto] diff --git a/strands-agentcore-smithy/scripts/deploy.sh b/strands-agentcore-smithy/scripts/deploy.sh new file mode 100755 index 0000000000..9d17a4df28 --- /dev/null +++ b/strands-agentcore-smithy/scripts/deploy.sh @@ -0,0 +1,240 @@ +#!/usr/bin/env bash +set -euo pipefail + +# ============================================================ +# AgentCore Smithy Bedrock - Deployment Script (AWS SAM) +# ============================================================ +# Orchestrates the full deployment lifecycle with AWS SAM: +# 1. Validate the SAM template +# 2. Upload the official Smithy model to S3 +# 3. Build the Lambda with SAM (Makefile build — no Docker) +# 4. Deploy the stack with SAM +# 5. Create a Cognito test user +# 6. Generate scripts/test.sh +# ============================================================ + +STACK_NAME="${STACK_NAME:-agentcore-smithy-bedrock}" +REGION="${AWS_REGION:-us-east-1}" +TEMPLATE_PATH="infrastructure/template.yaml" +TEST_USERNAME="testuser" +TEST_PASSWORD="TestPass123!" + +echo "============================================================" +echo "Deploying stack: ${STACK_NAME} in region: ${REGION}" +echo "============================================================" + +# ============================================================ +# Step 1: Validate the SAM template +# ============================================================ +echo "" +echo ">>> Step 1: Validating SAM template..." +sam validate \ + --template-file "${TEMPLATE_PATH}" \ + --region "${REGION}" > /dev/null +echo "Template validation successful." + +# ============================================================ +# Step 2: Upload official Smithy model to S3 +# ============================================================ +echo "" +echo ">>> Step 2: Uploading Bedrock Runtime Smithy model to S3..." +SMITHY_BUCKET="${STACK_NAME}-smithy-models" +SMITHY_MODEL_URL="https://raw.githubusercontent.com/aws/api-models-aws/main/models/bedrock-runtime/service/2023-09-30/bedrock-runtime-2023-09-30.json" +SMITHY_MODEL_FILE="/tmp/bedrock-runtime-2023-09-30.json" + +curl -sL "${SMITHY_MODEL_URL}" -o "${SMITHY_MODEL_FILE}" +aws s3 mb "s3://${SMITHY_BUCKET}" --region "${REGION}" 2>/dev/null || true +aws s3 cp "${SMITHY_MODEL_FILE}" "s3://${SMITHY_BUCKET}/bedrock-runtime-2023-09-30.json" --region "${REGION}" +echo "Smithy model uploaded to s3://${SMITHY_BUCKET}/bedrock-runtime-2023-09-30.json" + +# ============================================================ +# Step 3: Build the Lambda with SAM (Makefile build, no Docker) +# ============================================================ +echo "" +echo ">>> Step 3: Building Lambda with SAM (Makefile build)..." +sam build \ + --template-file "${TEMPLATE_PATH}" +echo "SAM build complete." + +# ============================================================ +# Step 4: Deploy the stack with SAM +# ============================================================ +echo "" +echo ">>> Step 4: Deploying stack with SAM..." +sam deploy \ + --stack-name "${STACK_NAME}" \ + --region "${REGION}" \ + --capabilities CAPABILITY_NAMED_IAM \ + --resolve-s3 \ + --no-confirm-changeset \ + --no-fail-on-empty-changeset \ + --parameter-overrides "GatewayName=smithy-bedrock-gateway" +echo "SAM deploy complete." + +# ============================================================ +# Retrieve stack outputs +# ============================================================ +echo "" +echo ">>> Retrieving stack outputs..." + +GATEWAY_ID=$(aws cloudformation describe-stacks \ + --stack-name "${STACK_NAME}" \ + --region "${REGION}" \ + --query "Stacks[0].Outputs[?OutputKey=='GatewayId'].OutputValue" \ + --output text) + +COGNITO_USER_POOL_ID=$(aws cloudformation describe-stacks \ + --stack-name "${STACK_NAME}" \ + --region "${REGION}" \ + --query "Stacks[0].Outputs[?OutputKey=='CognitoUserPoolId'].OutputValue" \ + --output text) + +COGNITO_CLIENT_ID=$(aws cloudformation describe-stacks \ + --stack-name "${STACK_NAME}" \ + --region "${REGION}" \ + --query "Stacks[0].Outputs[?OutputKey=='CognitoClientId'].OutputValue" \ + --output text) + +LAMBDA_FUNCTION_NAME=$(aws cloudformation describe-stacks \ + --stack-name "${STACK_NAME}" \ + --region "${REGION}" \ + --query "Stacks[0].Outputs[?OutputKey=='LambdaFunctionName'].OutputValue" \ + --output text) + +echo "Gateway ID: ${GATEWAY_ID}" +echo "Cognito User Pool ID: ${COGNITO_USER_POOL_ID}" +echo "Cognito Client ID: ${COGNITO_CLIENT_ID}" +echo "Lambda Function: ${LAMBDA_FUNCTION_NAME}" + +# ============================================================ +# Step 5: Create Cognito test user +# ============================================================ +echo "" +echo ">>> Step 5: Creating Cognito test user..." + +aws cognito-idp admin-create-user \ + --user-pool-id "${COGNITO_USER_POOL_ID}" \ + --username "${TEST_USERNAME}" \ + --temporary-password "${TEST_PASSWORD}" \ + --message-action SUPPRESS \ + --region "${REGION}" 2>/dev/null || echo "User may already exist, continuing..." + +aws cognito-idp admin-set-user-password \ + --user-pool-id "${COGNITO_USER_POOL_ID}" \ + --username "${TEST_USERNAME}" \ + --password "${TEST_PASSWORD}" \ + --permanent \ + --region "${REGION}" + +echo "Cognito test user created (username: ${TEST_USERNAME})." + +# ============================================================ +# Step 6: Generate test script (scripts/test.sh) +# ============================================================ +echo "" +echo ">>> Step 6: Generating test script..." + +cat > scripts/test.sh << 'TESTSCRIPT_EOF' +#!/usr/bin/env bash +set -euo pipefail + +# ============================================================ +# AgentCore Smithy Bedrock - Test Script +# Generated by deploy.sh — do not edit manually +# ============================================================ + +TESTSCRIPT_EOF + +# Append baked-in configuration values (not inside heredoc to allow variable expansion) +cat >> scripts/test.sh << BAKED_VALUES_EOF +REGION="${REGION}" +GATEWAY_ID="${GATEWAY_ID}" +COGNITO_USER_POOL_ID="${COGNITO_USER_POOL_ID}" +COGNITO_CLIENT_ID="${COGNITO_CLIENT_ID}" +LAMBDA_FUNCTION_NAME="${LAMBDA_FUNCTION_NAME}" +TEST_USERNAME="${TEST_USERNAME}" +TEST_PASSWORD="${TEST_PASSWORD}" +BAKED_VALUES_EOF + +# Append the rest of the script logic using a non-expanding heredoc +cat >> scripts/test.sh << 'TESTSCRIPT_LOGIC_EOF' + +PROMPT="${1:-Use the invoke model tool to ask Claude Haiku to write a short poem about the Beatles}" + +echo "============================================================" +echo "Testing AgentCore Smithy Bedrock Agent" +echo "============================================================" +echo "Region: ${REGION}" +echo "Lambda: ${LAMBDA_FUNCTION_NAME}" +echo "Prompt: ${PROMPT}" +echo "" + +# Authenticate with Cognito +echo ">>> Authenticating with Cognito..." +AUTH_RESULT=$(aws cognito-idp initiate-auth \ + --auth-flow USER_PASSWORD_AUTH \ + --client-id "${COGNITO_CLIENT_ID}" \ + --auth-parameters USERNAME="${TEST_USERNAME}",PASSWORD="${TEST_PASSWORD}" \ + --region "${REGION}" \ + --query "AuthenticationResult.IdToken" \ + --output text) + +ID_TOKEN="${AUTH_RESULT}" +echo "Authentication successful." + +# Build the Lambda payload +PAYLOAD_FILE="/tmp/test-payload-$$.json" +rm -f "${PAYLOAD_FILE}" +cat > "${PAYLOAD_FILE}" << PAYLOAD_INNER_EOF +{ + "headers": { + "Authorization": "Bearer ${ID_TOKEN}" + }, + "body": { + "prompt": "${PROMPT}" + } +} +PAYLOAD_INNER_EOF + +# Invoke the Lambda function +echo "" +echo ">>> Invoking Lambda function..." +OUTPUT_FILE="/tmp/test-output-$$.json" +rm -f "${OUTPUT_FILE}" + +aws lambda invoke \ + --function-name "${LAMBDA_FUNCTION_NAME}" \ + --payload "file://${PAYLOAD_FILE}" \ + --region "${REGION}" \ + --cli-binary-format raw-in-base64-out \ + "${OUTPUT_FILE}" > /dev/null + +echo "" +echo ">>> Response:" +cat "${OUTPUT_FILE}" +echo "" + +# Cleanup +rm -f "${PAYLOAD_FILE}" "${OUTPUT_FILE}" + +echo "" +echo "============================================================" +echo "Test complete." +echo "============================================================" +TESTSCRIPT_LOGIC_EOF + +chmod +x scripts/test.sh +echo "Test script generated at scripts/test.sh" + +# ============================================================ +# Deployment complete +# ============================================================ +echo "" +echo "============================================================" +echo "Deployment complete!" +echo "============================================================" +echo "" +echo "To test the agent, run:" +echo " ./scripts/test.sh" +echo " ./scripts/test.sh \"Use the invoke model tool to ask Haiku to write a short poem about the Beatles\"" +echo "" diff --git a/strands-agentcore-smithy/scripts/test.sh b/strands-agentcore-smithy/scripts/test.sh new file mode 100755 index 0000000000..bed5ae17b4 --- /dev/null +++ b/strands-agentcore-smithy/scripts/test.sh @@ -0,0 +1,78 @@ +#!/usr/bin/env bash +set -euo pipefail + +# ============================================================ +# AgentCore Smithy Bedrock - Test Script +# Generated by deploy.sh — do not edit manually +# ============================================================ + +REGION="us-east-1" +GATEWAY_ID="smithy-bedrock-gateway-2icnnwzik5" +COGNITO_USER_POOL_ID="us-east-1_d0B5LdMQN" +COGNITO_CLIENT_ID="c2ns12guk72f41kvsqs9oqs6u" +LAMBDA_FUNCTION_NAME="agentcore-smithy-bedrock-AgentFunction" +TEST_USERNAME="testuser" +TEST_PASSWORD="TestPass123!" + +PROMPT="${1:-Use the invoke model tool to ask Claude Haiku to write a short poem about the Beatles}" + +echo "============================================================" +echo "Testing AgentCore Smithy Bedrock Agent" +echo "============================================================" +echo "Region: ${REGION}" +echo "Lambda: ${LAMBDA_FUNCTION_NAME}" +echo "Prompt: ${PROMPT}" +echo "" + +# Authenticate with Cognito +echo ">>> Authenticating with Cognito..." +AUTH_RESULT=$(aws cognito-idp initiate-auth \ + --auth-flow USER_PASSWORD_AUTH \ + --client-id "${COGNITO_CLIENT_ID}" \ + --auth-parameters USERNAME="${TEST_USERNAME}",PASSWORD="${TEST_PASSWORD}" \ + --region "${REGION}" \ + --query "AuthenticationResult.IdToken" \ + --output text) + +ID_TOKEN="${AUTH_RESULT}" +echo "Authentication successful." + +# Build the Lambda payload +PAYLOAD_FILE="/tmp/test-payload-$$.json" +rm -f "${PAYLOAD_FILE}" +cat > "${PAYLOAD_FILE}" << PAYLOAD_INNER_EOF +{ + "headers": { + "Authorization": "Bearer ${ID_TOKEN}" + }, + "body": { + "prompt": "${PROMPT}" + } +} +PAYLOAD_INNER_EOF + +# Invoke the Lambda function +echo "" +echo ">>> Invoking Lambda function..." +OUTPUT_FILE="/tmp/test-output-$$.json" +rm -f "${OUTPUT_FILE}" + +aws lambda invoke \ + --function-name "${LAMBDA_FUNCTION_NAME}" \ + --payload "file://${PAYLOAD_FILE}" \ + --region "${REGION}" \ + --cli-binary-format raw-in-base64-out \ + "${OUTPUT_FILE}" > /dev/null + +echo "" +echo ">>> Response:" +cat "${OUTPUT_FILE}" +echo "" + +# Cleanup +rm -f "${PAYLOAD_FILE}" "${OUTPUT_FILE}" + +echo "" +echo "============================================================" +echo "Test complete." +echo "============================================================" diff --git a/strands-agentcore-smithy/src/agent/__init__.py b/strands-agentcore-smithy/src/agent/__init__.py new file mode 100644 index 0000000000..2330772da6 --- /dev/null +++ b/strands-agentcore-smithy/src/agent/__init__.py @@ -0,0 +1,4 @@ +"""Agent modules for the Lambda function. + +Provides target-type agnostic handler, processor, and client factories. +""" diff --git a/strands-agentcore-smithy/src/agent/agent_processor.py b/strands-agentcore-smithy/src/agent/agent_processor.py new file mode 100644 index 0000000000..6ae57949df --- /dev/null +++ b/strands-agentcore-smithy/src/agent/agent_processor.py @@ -0,0 +1,77 @@ +"""Agent request processor with MCP session lifecycle management. + +Target-type agnostic processor that creates a Strands Agent with an MCP +client connected to AgentCore Gateway. Manages the MCP session in a +try/finally block to ensure cleanup on both success and failure paths. +""" + +import os + +from strands import Agent + +from src.agent.strands_client import create_bedrock_model, create_mcp_client +from src.shared.error_utils import format_internal_error_response +from src.shared.logging_utils import get_logger +from src.shared.models import AgentRequest, AgentResponse + +logger = get_logger(__name__) + +# Environment variable for the gateway URL +GATEWAY_URL_ENV = "GATEWAY_URL" + +SYSTEM_PROMPT = """You have access to Bedrock Runtime tools via MCP. Use the Converse tool (not InvokeModel) to call another model. + +When using bedrock-runtime-target___Converse: +- Set modelId to: anthropic.claude-haiku-4-5-20251001-v1:0 +- Set messages to an array with one object: [{"role": "user", "content": [{"text": "YOUR PROMPT HERE"}]}] +- The content field inside each message must be an array of objects with a "text" key +- Do NOT set Region, UseFIPS, UseDualStack, or Endpoint +- Only try ONCE — do not retry if it fails""" + + +def process_request(request: AgentRequest, gateway_url: str | None = None) -> AgentResponse: + """Process an agent request by invoking the Strands Agent with MCP tools. + + Creates an MCP client connected to the AgentCore Gateway, builds a Strands + Agent with a Bedrock model, and invokes the agent with the user's prompt. + The MCP session lifecycle is managed in a try/finally block — NOT a with + context manager — to ensure cleanup occurs even on exceptions. + + Args: + request: The agent request containing the prompt and user context. + gateway_url: Optional gateway URL override. If not provided, reads + from the GATEWAY_URL environment variable. + + Returns: + An AgentResponse with the agent's response on success, or an error + response on failure. + """ + url = gateway_url or os.environ.get(GATEWAY_URL_ENV) + if not url: + logger.error("Gateway URL not configured") + return format_internal_error_response("Gateway URL not configured") + + token = request.user_context.token + prompt = request.prompt + + logger.info("Processing request for user: %s", request.user_context.username) + + mcp_client = create_mcp_client(url, token) + try: + bedrock_model = create_bedrock_model() + agent = Agent(model=bedrock_model, tools=[mcp_client], system_prompt=SYSTEM_PROMPT) + result = agent(prompt) + logger.info("Agent completed successfully for user: %s", request.user_context.username) + return AgentResponse(success=True, response=str(result)) + except Exception as e: + logger.error("Agent processing failed: %s", e) + return format_internal_error_response("Agent processing error", exception=e) + finally: + logger.info("Cleaning up MCP client") + try: + mcp_client.cleanup() + except AttributeError: + try: + mcp_client.close() + except AttributeError: + pass diff --git a/strands-agentcore-smithy/src/agent/handler.py b/strands-agentcore-smithy/src/agent/handler.py new file mode 100644 index 0000000000..3db24815ae --- /dev/null +++ b/strands-agentcore-smithy/src/agent/handler.py @@ -0,0 +1,108 @@ +"""Lambda entry point for the agent function. + +Target-type agnostic handler that extracts JWT tokens from the Lambda event, +validates them, creates a UserContext, and delegates processing to the +agent_processor module. Returns AgentResponse as a dict suitable for +Lambda response serialization. +""" + +from dataclasses import asdict + +from src.agent.agent_processor import process_request +from src.shared.error_utils import ( + format_internal_error_response, + format_unauthorized_response, +) +from src.shared.jwt_utils import JWTValidationError, extract_username, validate_token +from src.shared.logging_utils import configure_logging, get_correlation_id +from src.shared.models import AgentRequest, UserContext + + +def _extract_token(event: dict) -> str | None: + """Extract JWT token from the Lambda event. + + Checks for the token in two locations: + 1. event["token"] — direct token field + 2. event["headers"]["Authorization"] — Bearer token from headers + + Args: + event: The Lambda event dictionary. + + Returns: + The JWT token string, or None if not found. + """ + token = event.get("token") + if token: + return token + + headers = event.get("headers", {}) + auth_header = headers.get("Authorization", "") + if auth_header.startswith("Bearer "): + return auth_header[len("Bearer "):] + + # Return raw Authorization header if present (non-Bearer) + return auth_header or None + + +def lambda_handler(event: dict, context) -> dict: + """Lambda entry point for agent invocations. + + Extracts and validates the JWT token from the event, builds an + AgentRequest with the user's prompt and context, and delegates + to the agent processor. Returns the AgentResponse as a dict. + + Args: + event: Lambda event containing token/headers and prompt. + context: Lambda context object (unused). + + Returns: + A dict representation of AgentResponse. + """ + correlation_id = get_correlation_id(event) + logger = configure_logging(correlation_id=correlation_id) + + logger.info("Agent handler invoked") + + # Extract JWT token + token = _extract_token(event) + if not token: + logger.warning("Missing JWT token in request") + return asdict(format_unauthorized_response("Missing authentication token")) + + # Validate JWT and extract username + try: + claims = validate_token(token) + username = extract_username(claims) + except JWTValidationError as e: + logger.warning("JWT validation failed: %s", e) + return asdict(format_unauthorized_response(str(e))) + + # Build request and process + user_context = UserContext(username=username, token=token) + prompt = event.get("prompt", "") + + # Also check nested body.prompt (from test script payloads) + if not prompt: + body = event.get("body", {}) + if isinstance(body, dict): + prompt = body.get("prompt", "") + elif isinstance(body, str): + import json + try: + body = json.loads(body) + prompt = body.get("prompt", "") + except (json.JSONDecodeError, AttributeError): + pass + + if not prompt: + logger.warning("Empty prompt received from user: %s", username) + return asdict(format_unauthorized_response("Missing prompt")) + + request = AgentRequest(prompt=prompt, user_context=user_context) + + try: + response = process_request(request) + return asdict(response) + except Exception as e: + logger.error("Unexpected error processing request: %s", e) + return asdict(format_internal_error_response("Unexpected error", exception=e)) diff --git a/strands-agentcore-smithy/src/agent/strands_client.py b/strands-agentcore-smithy/src/agent/strands_client.py new file mode 100644 index 0000000000..ccf4e73729 --- /dev/null +++ b/strands-agentcore-smithy/src/agent/strands_client.py @@ -0,0 +1,71 @@ +"""Factory functions for Strands SDK client configuration. + +Target-type agnostic factories for creating MCP clients and Bedrock model +instances. These functions connect to AgentCore Gateway via MCP protocol +and are unaware of the target type behind the gateway. +""" + +import logging + +from mcp.client.streamable_http import streamablehttp_client +from strands.models.bedrock import BedrockModel +from strands.tools.mcp import MCPClient + +logger = logging.getLogger(__name__) + +# Default Bedrock model ID for Claude Sonnet 4.6 +DEFAULT_MODEL_ID = "us.anthropic.claude-sonnet-4-6" + +# Default AWS region for Bedrock +DEFAULT_REGION = "us-east-1" + + +def create_mcp_client(gateway_url: str, token: str) -> MCPClient: + """Create an MCP client configured to connect to AgentCore Gateway. + + Uses streamable HTTP transport with a Bearer token for authentication. + The returned client is NOT used as a context manager — the Agent manages + the MCP session lifecycle, and cleanup should occur in a finally block. + + Args: + gateway_url: The AgentCore Gateway endpoint URL. + token: JWT token for gateway authentication. + + Returns: + An MCPClient instance configured for the gateway. + """ + logger.info("Creating MCP client for gateway: %s", gateway_url) + + mcp_client = MCPClient( + lambda: streamablehttp_client( + gateway_url, + headers={"Authorization": f"Bearer {token}"}, + ) + ) + + return mcp_client + + +def create_bedrock_model( + model_id: str = DEFAULT_MODEL_ID, + region_name: str = DEFAULT_REGION, +) -> BedrockModel: + """Create a Bedrock model instance for the Strands Agent. + + Configures Claude 3 Sonnet as the LLM provider via AWS Bedrock. + + Args: + model_id: Bedrock model identifier. Defaults to Claude 3 Sonnet. + region_name: AWS region for Bedrock API calls. Defaults to us-east-1. + + Returns: + A BedrockModel instance ready for use with a Strands Agent. + """ + logger.info("Creating Bedrock model: %s in %s", model_id, region_name) + + model = BedrockModel( + model_id=model_id, + region_name=region_name, + ) + + return model diff --git a/strands-agentcore-smithy/src/shared/__init__.py b/strands-agentcore-smithy/src/shared/__init__.py new file mode 100644 index 0000000000..9853d4aab7 --- /dev/null +++ b/strands-agentcore-smithy/src/shared/__init__.py @@ -0,0 +1,36 @@ +"""Shared modules for the agent application. + +Provides target-type agnostic data models, utilities, and helpers. +""" + +from src.shared.models import AgentRequest, AgentResponse, UserContext +from src.shared.jwt_utils import ( + JWTValidationError, + extract_username, + validate_token, +) +from src.shared.error_utils import ( + format_error_response, + format_internal_error_response, + format_unauthorized_response, +) +from src.shared.logging_utils import ( + configure_logging, + get_correlation_id, + get_logger, +) + +__all__ = [ + "UserContext", + "AgentRequest", + "AgentResponse", + "JWTValidationError", + "extract_username", + "validate_token", + "format_error_response", + "format_internal_error_response", + "format_unauthorized_response", + "configure_logging", + "get_correlation_id", + "get_logger", +] diff --git a/strands-agentcore-smithy/src/shared/error_utils.py b/strands-agentcore-smithy/src/shared/error_utils.py new file mode 100644 index 0000000000..ed45210270 --- /dev/null +++ b/strands-agentcore-smithy/src/shared/error_utils.py @@ -0,0 +1,75 @@ +"""Standardized error response formatting. + +Target-type agnostic error handling utilities that produce consistent +AgentResponse objects for error conditions across the agent application. +""" + +import logging +from typing import Optional + +from src.shared.models import AgentResponse + +logger = logging.getLogger(__name__) + + +def format_error_response( + error_message: str, + status_code: int = 500, + log_message: Optional[str] = None, +) -> AgentResponse: + """Create a standardized error AgentResponse. + + Logs the error and returns a consistent AgentResponse with success=False. + + Args: + error_message: User-facing error description. + status_code: HTTP-style status code for categorization (default 500). + log_message: Optional detailed message for logging. If not provided, + the error_message is logged. + + Returns: + An AgentResponse with success=False and the error details. + """ + log_msg = log_message or error_message + if status_code >= 500: + logger.error("Error [%d]: %s", status_code, log_msg) + else: + logger.warning("Error [%d]: %s", status_code, log_msg) + + return AgentResponse( + success=False, + response="", + error=error_message, + ) + + +def format_unauthorized_response(detail: str = "Unauthorized") -> AgentResponse: + """Create a 401 Unauthorized error response. + + Args: + detail: Specific unauthorized reason (default "Unauthorized"). + + Returns: + An AgentResponse for authentication failures. + """ + return format_error_response(detail, status_code=401) + + +def format_internal_error_response( + detail: str = "Internal server error", + exception: Optional[Exception] = None, +) -> AgentResponse: + """Create a 500 Internal Server Error response. + + Optionally logs the full exception for debugging while returning + a safe user-facing message. + + Args: + detail: User-facing error description. + exception: Optional exception to log for debugging. + + Returns: + An AgentResponse for internal server errors. + """ + log_msg = f"{detail}: {exception}" if exception else detail + return format_error_response(detail, status_code=500, log_message=log_msg) diff --git a/strands-agentcore-smithy/src/shared/jwt_utils.py b/strands-agentcore-smithy/src/shared/jwt_utils.py new file mode 100644 index 0000000000..f03dcc28de --- /dev/null +++ b/strands-agentcore-smithy/src/shared/jwt_utils.py @@ -0,0 +1,96 @@ +"""JWT validation utilities for Cognito token handling. + +Target-type agnostic JWT validation that works with any AgentCore Gateway +target type. Accepts both access and ID tokens, disables audience verification +(gateway handles it via AllowedAudience), and extracts the cognito:username claim. +""" + +import logging +from typing import Any, Dict + +import jwt + +logger = logging.getLogger(__name__) + +# Valid token_use claim values accepted by the validator +VALID_TOKEN_USE_VALUES = {"access", "id"} + + +class JWTValidationError(Exception): + """Raised when JWT validation fails.""" + pass + + +def validate_token(token: str, options: Dict[str, Any] | None = None) -> Dict[str, Any]: + """Decode and validate a JWT token. + + Decodes the token without signature verification (gateway handles full + verification via CUSTOM_JWT authorizer). Validates that the token_use + claim is either 'access' or 'id'. Disables audience verification since + the gateway validates audience via AllowedAudience. + + Args: + token: The JWT token string to validate. + options: Optional PyJWT decode options override. + + Returns: + The decoded JWT claims dictionary. + + Raises: + JWTValidationError: If the token is invalid, expired, or has an + unsupported token_use claim. + """ + if not token: + raise JWTValidationError("Token is empty or missing") + + decode_options = options or { + "verify_signature": False, + "verify_aud": False, + "verify_exp": True, + } + + try: + claims = jwt.decode( + token, + algorithms=["RS256"], + options=decode_options, + ) + except jwt.ExpiredSignatureError: + logger.warning("JWT token has expired") + raise JWTValidationError("Token has expired") + except jwt.DecodeError as e: + logger.warning("JWT decode error: %s", str(e)) + raise JWTValidationError(f"Invalid token: {e}") + except jwt.InvalidTokenError as e: + logger.warning("JWT validation error: %s", str(e)) + raise JWTValidationError(f"Token validation failed: {e}") + + # Validate token_use claim — accept both 'access' and 'id' + token_use = claims.get("token_use") + if token_use is not None and token_use not in VALID_TOKEN_USE_VALUES: + raise JWTValidationError( + f"Unsupported token_use: {token_use}. Must be one of: {VALID_TOKEN_USE_VALUES}" + ) + + return claims + + +def extract_username(claims: Dict[str, Any]) -> str: + """Extract the username from JWT claims. + + Uses the 'cognito:username' claim, which is the correct claim for + Cognito ID tokens. Does NOT fall back to 'username' or 'sub' claims. + + Args: + claims: Decoded JWT claims dictionary. + + Returns: + The username string from the cognito:username claim. + + Raises: + JWTValidationError: If the cognito:username claim is missing. + """ + username = claims.get("cognito:username") + if not username: + raise JWTValidationError("Missing 'cognito:username' claim in token") + return username diff --git a/strands-agentcore-smithy/src/shared/logging_utils.py b/strands-agentcore-smithy/src/shared/logging_utils.py new file mode 100644 index 0000000000..73199b4e74 --- /dev/null +++ b/strands-agentcore-smithy/src/shared/logging_utils.py @@ -0,0 +1,74 @@ +"""Structured logging with correlation IDs. + +Target-type agnostic logging utilities that provide consistent structured +logging across the agent application. Supports correlation IDs for request +tracing through Lambda invocations. +""" + +import logging +import uuid +from typing import Any, Dict, Optional + + +def get_correlation_id(event: Optional[Dict[str, Any]] = None) -> str: + """Extract or generate a correlation ID for request tracing. + + Attempts to extract a correlation ID from the Lambda event's request + context. Falls back to generating a new UUID if none is found. + + Args: + event: Optional Lambda event dictionary. + + Returns: + A correlation ID string. + """ + if event: + # Check for AWS request ID in the event context + request_context = event.get("requestContext", {}) + request_id = request_context.get("requestId") + if request_id: + return request_id + + return str(uuid.uuid4()) + + +def configure_logging( + level: int = logging.INFO, + correlation_id: Optional[str] = None, +) -> logging.Logger: + """Configure structured logging for the application. + + Sets up the root logger with a structured format that includes + the correlation ID for request tracing. + + Args: + level: Logging level (default INFO). + correlation_id: Optional correlation ID to include in log format. + + Returns: + The configured root logger. + """ + log_format = "[%(levelname)s]" + if correlation_id: + log_format += f" [{correlation_id}]" + log_format += " %(name)s - %(message)s" + + logging.basicConfig(level=level, format=log_format, force=True) + # Enable debug logging for strands to see tool call details + logging.getLogger("strands").setLevel(logging.DEBUG) + return logging.getLogger() + + +def get_logger(name: str) -> logging.Logger: + """Get a named logger instance. + + Convenience wrapper around logging.getLogger for consistent + logger creation across modules. + + Args: + name: Logger name, typically __name__ of the calling module. + + Returns: + A named Logger instance. + """ + return logging.getLogger(name) diff --git a/strands-agentcore-smithy/src/shared/models.py b/strands-agentcore-smithy/src/shared/models.py new file mode 100644 index 0000000000..5f02724b5c --- /dev/null +++ b/strands-agentcore-smithy/src/shared/models.py @@ -0,0 +1,29 @@ +"""Shared data models for the agent application. + +Target-type agnostic data classes used across agent and shared modules. +""" + +from dataclasses import dataclass +from typing import Optional + + +@dataclass +class UserContext: + """Represents an authenticated user's context.""" + username: str + token: str + + +@dataclass +class AgentRequest: + """Represents a request to the agent.""" + prompt: str + user_context: UserContext + + +@dataclass +class AgentResponse: + """Represents a response from the agent.""" + success: bool + response: str + error: Optional[str] = None diff --git a/strands-agentcore-smithy/tests/__init__.py b/strands-agentcore-smithy/tests/__init__.py new file mode 100644 index 0000000000..65140f2e39 --- /dev/null +++ b/strands-agentcore-smithy/tests/__init__.py @@ -0,0 +1 @@ +# tests package diff --git a/strands-agentcore-smithy/tests/conftest.py b/strands-agentcore-smithy/tests/conftest.py new file mode 100644 index 0000000000..aec84a94fe --- /dev/null +++ b/strands-agentcore-smithy/tests/conftest.py @@ -0,0 +1,140 @@ +"""Shared test fixtures for the agentcore-smithy-bedrock project.""" + +import json +import os +import sys +import time + +import pytest +import jwt +import yaml + +# Add src to path so shared modules are importable +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src")) + +_ROOT = os.path.join(os.path.dirname(__file__), "..") + +# --------------------------------------------------------------------------- +# Custom YAML loader for CloudFormation intrinsic functions +# --------------------------------------------------------------------------- + +class _CfnLoader(yaml.SafeLoader): + pass + + +def _cfn_tag_constructor(loader, tag_suffix, node): + if isinstance(node, yaml.ScalarNode): + return loader.construct_scalar(node) + if isinstance(node, yaml.SequenceNode): + return loader.construct_sequence(node) + if isinstance(node, yaml.MappingNode): + return loader.construct_mapping(node) + return None + + +_CfnLoader.add_multi_constructor("!", _cfn_tag_constructor) + +# --------------------------------------------------------------------------- +# CloudFormation template fixtures +# --------------------------------------------------------------------------- + +@pytest.fixture(scope="session") +def cfn_template(): + """Load and return the parsed SAM/CloudFormation template.""" + path = os.path.join(_ROOT, "infrastructure", "template.yaml") + with open(path) as fh: + return yaml.load(fh, Loader=_CfnLoader) + + +@pytest.fixture(scope="session") +def cfn_resources(cfn_template): + """Return the Resources section of the CloudFormation template.""" + return cfn_template["Resources"] + + +@pytest.fixture(scope="session") +def cfn_outputs(cfn_template): + """Return the Outputs section of the CloudFormation template.""" + return cfn_template.get("Outputs", {}) + + +# --------------------------------------------------------------------------- +# Smithy model fixtures +# --------------------------------------------------------------------------- + +@pytest.fixture(scope="session") +def smithy_model(cfn_resources): + """Parse and return the official AWS Smithy model.""" + model_path = os.path.join( + os.path.dirname(__file__), "..", "infrastructure", "bedrock-runtime-2023-09-30.json" + ) + if not os.path.exists(model_path): + model_path = "/tmp/bedrock-runtime-2023-09-30.json" + with open(model_path) as f: + return json.load(f) + + +@pytest.fixture(scope="session") +def smithy_shapes(smithy_model): + """Return the shapes dict from the Smithy model.""" + return smithy_model["shapes"] + + +# --------------------------------------------------------------------------- +# Deploy script fixtures +# --------------------------------------------------------------------------- + +@pytest.fixture(scope="session") +def deploy_script_content(): + """Return the raw content of scripts/deploy.sh.""" + path = os.path.join(_ROOT, "scripts", "deploy.sh") + with open(path) as fh: + return fh.read() + + +# --------------------------------------------------------------------------- +# JWT / user context fixtures +# --------------------------------------------------------------------------- + +@pytest.fixture +def make_jwt_token(): + """Factory fixture that creates JWT tokens with given claims.""" + + def _make(claims: dict, exp_offset: int = 3600) -> str: + payload = { + "iat": int(time.time()), + "exp": int(time.time()) + exp_offset, + **claims, + } + return jwt.encode(payload, "test-secret", algorithm="HS256") + + return _make + + +@pytest.fixture +def sample_id_token(make_jwt_token): + """A sample Cognito ID token with cognito:username claim.""" + return make_jwt_token({ + "token_use": "id", + "cognito:username": "testuser", + "sub": "abc-123", + }) + + +@pytest.fixture +def sample_access_token(make_jwt_token): + """A sample Cognito access token.""" + return make_jwt_token({ + "token_use": "access", + "username": "testuser", + "sub": "abc-123", + }) + + +@pytest.fixture +def sample_user_context(sample_id_token): + """A sample UserContext dict.""" + return { + "username": "testuser", + "token": sample_id_token, + } diff --git a/strands-agentcore-smithy/tests/property/__init__.py b/strands-agentcore-smithy/tests/property/__init__.py new file mode 100644 index 0000000000..b622bf8d9d --- /dev/null +++ b/strands-agentcore-smithy/tests/property/__init__.py @@ -0,0 +1 @@ +# property tests package diff --git a/strands-agentcore-smithy/tests/property/test_agnostic_properties.py b/strands-agentcore-smithy/tests/property/test_agnostic_properties.py new file mode 100644 index 0000000000..195c40fd6f --- /dev/null +++ b/strands-agentcore-smithy/tests/property/test_agnostic_properties.py @@ -0,0 +1,82 @@ +"""Property-based tests for agent code target-type agnosticism (Property 8). + +Feature: agentcore-smithy-bedrock +Property 8: Agent code is target-type agnostic + Validates: Requirements 11.1 + +Scans all Python source files in src/agent/ and src/shared/ to verify that +none contain DynamoDB-specific or Smithy-specific identifiers. +""" + +import os +import re + +from hypothesis import given, settings, assume +from hypothesis import strategies as st + +# --------------------------------------------------------------------------- +# Constants +# --------------------------------------------------------------------------- + +_SRC_ROOT = os.path.join(os.path.dirname(__file__), "..", "..", "src") + +# Identifiers that must NOT appear in agent/shared code. +# Case-insensitive patterns for DynamoDB and Smithy references. +_FORBIDDEN_PATTERNS: list[re.Pattern] = [ + re.compile(r"\bdynamodb\b", re.IGNORECASE), + re.compile(r"\bGetItem\b"), + re.compile(r"\bPutItem\b"), + re.compile(r"\bQuery\b"), + re.compile(r"\bTableName\b"), + re.compile(r"\bsmithy\b", re.IGNORECASE), + re.compile(r"\bSmithyModel\b"), + re.compile(r"\bInlinePayload\b"), +] + +_SOURCE_DIRS = ["agent", "shared"] + + +def _collect_source_files() -> list[str]: + """Return all .py files under src/agent/ and src/shared/.""" + files: list[str] = [] + for subdir in _SOURCE_DIRS: + dirpath = os.path.join(_SRC_ROOT, subdir) + if not os.path.isdir(dirpath): + continue + for fname in os.listdir(dirpath): + if fname.endswith(".py"): + files.append(os.path.join(dirpath, fname)) + return files + + +_SOURCE_FILES = _collect_source_files() +_SOURCE_CONTENTS: dict[str, str] = {} +for _f in _SOURCE_FILES: + with open(_f) as _fh: + _SOURCE_CONTENTS[_f] = _fh.read() + + +# --------------------------------------------------------------------------- +# Property 8: Agent code is target-type agnostic +# Tag: Feature: agentcore-smithy-bedrock, Property 8: Agent code is +# target-type agnostic +# --------------------------------------------------------------------------- + +# Strategy: pick a random source file and a random forbidden pattern, +# then assert the pattern does not appear in that file. + +source_file_st = st.sampled_from(list(_SOURCE_CONTENTS.keys())) +forbidden_pattern_st = st.sampled_from(_FORBIDDEN_PATTERNS) + + +@given(filepath=source_file_st, pattern=forbidden_pattern_st) +@settings(max_examples=100) +def test_property8_no_dynamodb_or_smithy_identifiers(filepath, pattern): + """For all source files in src/agent/ and src/shared/, none shall + contain DynamoDB-specific or Smithy-specific identifiers.""" + content = _SOURCE_CONTENTS[filepath] + matches = pattern.findall(content) + rel = os.path.relpath(filepath, _SRC_ROOT) + assert not matches, ( + f"{rel} contains forbidden identifier matching /{pattern.pattern}/: {matches}" + ) diff --git a/strands-agentcore-smithy/tests/property/test_cloudformation_properties.py b/strands-agentcore-smithy/tests/property/test_cloudformation_properties.py new file mode 100644 index 0000000000..fe5b75cf69 --- /dev/null +++ b/strands-agentcore-smithy/tests/property/test_cloudformation_properties.py @@ -0,0 +1,252 @@ +"""Property-based tests for CloudFormation template (Properties 1, 2, 3, 6). + +Feature: agentcore-smithy-bedrock +Property 1: Smithy operation structure completeness + Validates: Requirements 1.5, 1.6 +Property 2: Smithy documentation coverage + Validates: Requirements 1.7 +Property 3: No streaming or custom protocols in Smithy model + Validates: Requirements 1.9 +Property 6: No API Gateway resources in CloudFormation template + Validates: Requirements 7.4 +""" + +import json +import os + +import yaml +from hypothesis import given, settings +from hypothesis import strategies as st + +# --------------------------------------------------------------------------- +# Custom YAML loader for CloudFormation intrinsic functions +# --------------------------------------------------------------------------- + + +class _CfnLoader(yaml.SafeLoader): + """YAML loader that handles CloudFormation intrinsic function tags.""" + pass + + +def _cfn_tag_constructor(loader, tag_suffix, node): + """Generic constructor that returns the tag value as-is.""" + if isinstance(node, yaml.ScalarNode): + return loader.construct_scalar(node) + if isinstance(node, yaml.SequenceNode): + return loader.construct_sequence(node) + if isinstance(node, yaml.MappingNode): + return loader.construct_mapping(node) + return None + + +_CfnLoader.add_multi_constructor("!", _cfn_tag_constructor) + +# --------------------------------------------------------------------------- +# Load CloudFormation template and extract Smithy model +# --------------------------------------------------------------------------- + +_TEMPLATE_PATH = os.path.join( + os.path.dirname(__file__), "..", "..", "infrastructure", "template.yaml" +) + +with open(_TEMPLATE_PATH) as _fh: + _TEMPLATE = yaml.load(_fh, Loader=_CfnLoader) + +_RESOURCES = _TEMPLATE["Resources"] + +# Load the official AWS Smithy model from local file +_SMITHY_MODEL_PATH = os.path.join( + os.path.dirname(__file__), "..", "..", "infrastructure", "bedrock-runtime-2023-09-30.json" +) +if not os.path.exists(_SMITHY_MODEL_PATH): + _SMITHY_MODEL_PATH = "/tmp/bedrock-runtime-2023-09-30.json" +with open(_SMITHY_MODEL_PATH) as _smithy_fh: + _SMITHY_PAYLOAD = _smithy_fh.read() +_SMITHY_MODEL = json.loads(_SMITHY_PAYLOAD) +_SHAPES = _SMITHY_MODEL["shapes"] + +# Identify the service shape and collect all operations (direct + from resources) +_SERVICE_SHAPE = None +_SERVICE_KEY = None +for _key, _shape in _SHAPES.items(): + if _shape.get("type") == "service": + _SERVICE_SHAPE = _shape + _SERVICE_KEY = _key + break + +_OPERATION_TARGETS = [op["target"] for op in _SERVICE_SHAPE.get("operations", [])] +# Also collect operations from resources +for _res in _SERVICE_SHAPE.get("resources", []): + _res_shape = _SHAPES.get(_res["target"], {}) + for _op in _res_shape.get("operations", []): + _OPERATION_TARGETS.append(_op["target"]) + for _op in _res_shape.get("collectionOperations", []): + _OPERATION_TARGETS.append(_op["target"]) +_OPERATIONS = {t: _SHAPES[t] for t in _OPERATION_TARGETS if t in _SHAPES} + +# Collect all shape keys for documentation checks +_ALL_SHAPE_KEYS = list(_SHAPES.keys()) + +# Collect all CFN resource types +_RESOURCE_TYPES = [ + res.get("Type", "") for res in _RESOURCES.values() if isinstance(res, dict) +] + +# --------------------------------------------------------------------------- +# Property 1: Smithy operation structure completeness +# Tag: Feature: agentcore-smithy-bedrock, Property 1: Smithy operation +# structure completeness +# --------------------------------------------------------------------------- + +operation_st = st.sampled_from(list(_OPERATIONS.keys())) + + +@given(op_key=operation_st) +@settings(max_examples=100) +def test_property1_operation_has_valid_input_with_required_traits(op_key): + """For all operations in the Smithy model, each must have a valid input + structure with mandatory members marked with smithy.api#required.""" + op = _OPERATIONS[op_key] + input_target = op["input"]["target"] + assert input_target in _SHAPES, f"Input shape {input_target} not found" + + input_shape = _SHAPES[input_target] + assert input_shape["type"] == "structure", f"{input_target} is not a structure" + assert "members" in input_shape, f"{input_target} has no members" + + # Check if any members are marked required (not all operations require this) + required_members = [ + name + for name, member in input_shape["members"].items() + if "smithy.api#required" in member.get("traits", {}) + ] + # Official AWS models may have operations with no required members (e.g. list operations) + # Just verify the structure is valid + assert isinstance(input_shape["members"], dict) + + +@given(op_key=operation_st) +@settings(max_examples=100) +def test_property1_operation_has_valid_output_structure(op_key): + """For all operations in the Smithy model, each must have a valid output + structure that captures the DynamoDB response shape.""" + op = _OPERATIONS[op_key] + output_target = op["output"]["target"] + assert output_target in _SHAPES, f"Output shape {output_target} not found" + + output_shape = _SHAPES[output_target] + assert output_shape["type"] == "structure", f"{output_target} is not a structure" + assert "members" in output_shape, f"{output_target} has no members" + assert len(output_shape["members"]) > 0, f"{output_target} has no response members" + + +# --------------------------------------------------------------------------- +# Property 2: Smithy documentation coverage +# Tag: Feature: agentcore-smithy-bedrock, Property 2: Smithy documentation +# coverage +# --------------------------------------------------------------------------- + +shape_key_st = st.sampled_from(_ALL_SHAPE_KEYS) + + +@given(shape_key=shape_key_st) +@settings(max_examples=100) +def test_property2_all_shapes_have_documentation(shape_key): + """For service and operation shapes in the Smithy model, each should include a + smithy.api#documentation trait. (Not all helper shapes in official models have docs.)""" + shape = _SHAPES[shape_key] + # Only check service and operation shapes for documentation + if shape.get("type") not in ("service", "operation"): + return + traits = shape.get("traits", {}) + doc = traits.get("smithy.api#documentation") + assert doc is not None, f"Shape {shape_key} missing smithy.api#documentation trait" + assert isinstance(doc, str) and len(doc.strip()) > 0, ( + f"Shape {shape_key} has empty documentation" + ) + + +@given(op_key=operation_st) +@settings(max_examples=100) +def test_property2_operation_members_have_documentation(op_key): + """For all operations, every member in input and output structures must + have a smithy.api#documentation trait.""" + op = _OPERATIONS[op_key] + for io_key in ("input", "output"): + target = op[io_key]["target"] + shape = _SHAPES[target] + for member_name, member in shape.get("members", {}).items(): + traits = member.get("traits", {}) + doc = traits.get("smithy.api#documentation") + assert doc is not None, ( + f"{target}.{member_name} missing smithy.api#documentation" + ) + assert isinstance(doc, str) and len(doc.strip()) > 0, ( + f"{target}.{member_name} has empty documentation" + ) + + +# --------------------------------------------------------------------------- +# Property 3: No streaming or custom protocols in Smithy model +# Tag: Feature: agentcore-smithy-bedrock, Property 3: No streaming or +# custom protocols in Smithy model +# --------------------------------------------------------------------------- + +_ALLOWED_PROTOCOL = "aws.protocols#restJson1" + + +@given(op_key=operation_st) +@settings(max_examples=100) +def test_property3_no_streaming_traits(op_key): + """For all operations in the Smithy model, none shall have streaming + traits (smithy.api#streaming).""" + op = _OPERATIONS[op_key] + op_traits = op.get("traits", {}) + assert "smithy.api#streaming" not in op_traits, ( + f"Operation {op_key} has a streaming trait" + ) + + # Also check input/output shapes for streaming + for io_key in ("input", "output"): + target = op[io_key]["target"] + shape = _SHAPES[target] + shape_traits = shape.get("traits", {}) + assert "smithy.api#streaming" not in shape_traits, ( + f"{target} has a streaming trait" + ) + + +@given(shape_key=shape_key_st) +@settings(max_examples=100) +def test_property3_only_restjson1_protocol(shape_key): + """For all shapes, any protocol trait must be aws.protocols#restJson1. + No custom or alternative protocols are allowed.""" + shape = _SHAPES[shape_key] + traits = shape.get("traits", {}) + for trait_key in traits: + if "protocol" in trait_key.lower(): + assert trait_key == _ALLOWED_PROTOCOL, ( + f"Shape {shape_key} uses disallowed protocol trait: {trait_key}" + ) + + +# --------------------------------------------------------------------------- +# Property 6: No API Gateway resources in CloudFormation template +# Tag: Feature: agentcore-smithy-bedrock, Property 6: No API Gateway +# resources in CloudFormation template +# --------------------------------------------------------------------------- + +resource_type_st = st.sampled_from(_RESOURCE_TYPES) + + +@given(resource_type=resource_type_st) +@settings(max_examples=100) +def test_property6_no_api_gateway_resources(resource_type): + """For all resources in the CloudFormation template, none shall have a + type matching AWS::ApiGateway::* or AWS::ApiGatewayV2::*.""" + assert not resource_type.startswith("AWS::ApiGateway::"), ( + f"Found API Gateway v1 resource: {resource_type}" + ) + assert not resource_type.startswith("AWS::ApiGatewayV2::"), ( + f"Found API Gateway v2 resource: {resource_type}" + ) diff --git a/strands-agentcore-smithy/tests/property/test_deploy_script_properties.py b/strands-agentcore-smithy/tests/property/test_deploy_script_properties.py new file mode 100644 index 0000000000..f1f489ddf6 --- /dev/null +++ b/strands-agentcore-smithy/tests/property/test_deploy_script_properties.py @@ -0,0 +1,50 @@ +"""Property-based tests for the Makefile build (Property 7). + +Feature: agentcore-smithy-bedrock +Property 7: Lambda packaging uses pip3 exclusively + Validates: pip3-only packaging + +After the migration to AWS SAM, Lambda packaging (the pip install steps) +lives in the root Makefile, invoked by `sam build` via BuildMethod: makefile. +This property verifies every pip install invocation uses pip3, not bare pip. +""" + +import os +import re + +from hypothesis import given, settings +from hypothesis import strategies as st + +# --------------------------------------------------------------------------- +# Load Makefile content (packaging now lives here, not in deploy.sh) +# --------------------------------------------------------------------------- + +_MAKEFILE_PATH = os.path.join( + os.path.dirname(__file__), "..", "..", "Makefile" +) + +with open(_MAKEFILE_PATH) as _fh: + _MAKEFILE_CONTENT = _fh.read() + +# Find all pip/pip3 install invocations +_PIP_INVOCATIONS = re.findall(r"\b(pip3?)\s+install\b", _MAKEFILE_CONTENT) + +assert len(_PIP_INVOCATIONS) > 0, "No pip install commands found in Makefile" + +pip_invocation_st = st.sampled_from(_PIP_INVOCATIONS) + +# --------------------------------------------------------------------------- +# Property 7: Lambda packaging uses pip3 exclusively +# Tag: Feature: agentcore-smithy-bedrock, Property 7: Lambda packaging uses +# pip3 exclusively +# --------------------------------------------------------------------------- + + +@given(invocation=pip_invocation_st) +@settings(max_examples=100) +def test_property7_all_pip_commands_use_pip3(invocation): + """For all pip install commands in the Makefile, each command shall + use pip3 (not pip) as the executable name.""" + assert invocation == "pip3", ( + "Found bare 'pip' instead of 'pip3' in Makefile" + ) diff --git a/strands-agentcore-smithy/tests/property/test_jwt_properties.py b/strands-agentcore-smithy/tests/property/test_jwt_properties.py new file mode 100644 index 0000000000..dab5feb774 --- /dev/null +++ b/strands-agentcore-smithy/tests/property/test_jwt_properties.py @@ -0,0 +1,120 @@ +"""Property-based tests for JWT validation (Properties 4 and 5). + +Feature: agentcore-smithy-bedrock +Property 4: JWT validation accepts both token_use values + Validates: Requirements 4.4 +Property 5: Username extraction from cognito:username claim + Validates: Requirements 4.6 +""" + +import time + +import jwt +import pytest +from hypothesis import given, settings, assume +from hypothesis import strategies as st + +from shared.jwt_utils import validate_token, extract_username, JWTValidationError + + +# --------------------------------------------------------------------------- +# Strategies +# --------------------------------------------------------------------------- + +# Printable text that won't be empty, for usernames +username_st = st.text( + alphabet=st.characters(whitelist_categories=("L", "N", "P")), + min_size=1, + max_size=64, +).filter(lambda s: s.strip() != "") + +token_use_st = st.sampled_from(["access", "id"]) + + +def _encode(claims: dict, exp_offset: int = 3600) -> str: + """Encode a JWT with HS256 for testing (no signature verification).""" + payload = { + "iat": int(time.time()), + "exp": int(time.time()) + exp_offset, + **claims, + } + return jwt.encode(payload, "test-secret", algorithm="HS256") + + +# --------------------------------------------------------------------------- +# Property 4: JWT validation accepts both token_use values +# Tag: Feature: agentcore-smithy-bedrock, Property 4: JWT validation accepts +# both token_use values +# --------------------------------------------------------------------------- + + +@given(token_use=token_use_st) +@settings(max_examples=100) +def test_property4_validate_token_accepts_access_and_id(token_use): + """For any valid JWT with token_use set to 'access' or 'id', + validate_token shall accept the token without raising an error.""" + token = _encode({"token_use": token_use, "cognito:username": "testuser"}) + claims = validate_token(token) + assert claims["token_use"] == token_use + + +@given( + token_use=token_use_st, + extra_claims=st.fixed_dictionaries( + {}, + optional={ + "sub": st.uuids().map(str), + "email": st.emails(), + "custom:role": st.sampled_from(["admin", "user", "viewer"]), + }, + ), +) +@settings(max_examples=100) +def test_property4_validate_token_with_extra_claims(token_use, extra_claims): + """Acceptance of token_use holds regardless of other claims present.""" + all_claims = { + "token_use": token_use, + "cognito:username": "anyuser", + **extra_claims, + } + token = _encode(all_claims) + claims = validate_token(token) + assert claims["token_use"] == token_use + + +# --------------------------------------------------------------------------- +# Property 5: Username extraction from cognito:username claim +# Tag: Feature: agentcore-smithy-bedrock, Property 5: Username extraction +# from cognito:username claim +# --------------------------------------------------------------------------- + + +@given(cognito_username=username_st) +@settings(max_examples=100) +def test_property5_extracts_cognito_username(cognito_username): + """For any JWT containing a cognito:username claim, extract_username + shall return that claim's value.""" + claims = {"cognito:username": cognito_username} + assert extract_username(claims) == cognito_username + + +@given( + cognito_username=username_st, + other_username=username_st, + sub_value=st.uuids().map(str), +) +@settings(max_examples=100) +def test_property5_cognito_username_preferred_over_others( + cognito_username, other_username, sub_value +): + """extract_username returns cognito:username even when 'username' and + 'sub' claims are also present with different values.""" + assume(cognito_username != other_username) + claims = { + "cognito:username": cognito_username, + "username": other_username, + "sub": sub_value, + } + result = extract_username(claims) + assert result == cognito_username + assert result != other_username diff --git a/strands-agentcore-smithy/tests/unit/__init__.py b/strands-agentcore-smithy/tests/unit/__init__.py new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/strands-agentcore-smithy/tests/unit/__init__.py @@ -0,0 +1 @@ + diff --git a/strands-agentcore-smithy/tests/unit/test_agent_processor.py b/strands-agentcore-smithy/tests/unit/test_agent_processor.py new file mode 100644 index 0000000000..d812ea8379 --- /dev/null +++ b/strands-agentcore-smithy/tests/unit/test_agent_processor.py @@ -0,0 +1,143 @@ +"""Unit tests for the agent processor module. + +Tests MCP session lifecycle (try/finally), gateway URL resolution, +and error handling during agent invocation. + +Requirements: 5.4, 5.5 +""" + +import sys +from types import ModuleType +from unittest.mock import patch, MagicMock + +import pytest + +# Mock third-party modules not installed in test env +_strands_mod = ModuleType("strands") +_strands_mod.Agent = MagicMock() +_mcp_mod = ModuleType("mcp") +_mcp_client_mod = ModuleType("mcp.client") +_mcp_http_mod = ModuleType("mcp.client.streamable_http") +_mcp_http_mod.streamablehttp_client = MagicMock() +_bedrock_mod = ModuleType("strands.models") +_bedrock_bedrock_mod = ModuleType("strands.models.bedrock") +_bedrock_bedrock_mod.BedrockModel = MagicMock() +_strands_tools_mod = ModuleType("strands.tools") +_strands_tools_mcp_mod = ModuleType("strands.tools.mcp") +_strands_tools_mcp_mod.MCPClient = MagicMock() + +sys.modules.setdefault("strands", _strands_mod) +sys.modules.setdefault("strands.models", _bedrock_mod) +sys.modules.setdefault("strands.models.bedrock", _bedrock_bedrock_mod) +sys.modules.setdefault("strands.tools", _strands_tools_mod) +sys.modules.setdefault("strands.tools.mcp", _strands_tools_mcp_mod) +sys.modules.setdefault("mcp", _mcp_mod) +sys.modules.setdefault("mcp.client", _mcp_client_mod) +sys.modules.setdefault("mcp.client.streamable_http", _mcp_http_mod) + +from shared.models import UserContext, AgentRequest + + +def _make_request(prompt="test prompt", username="alice", token="jwt-tok"): + ctx = UserContext(username=username, token=token) + return AgentRequest(prompt=prompt, user_context=ctx) + + +class TestProcessRequest: + """Tests for process_request function.""" + + @patch("agent.agent_processor.Agent") + @patch("agent.agent_processor.create_bedrock_model") + @patch("agent.agent_processor.create_mcp_client") + def test_successful_invocation(self, mock_mcp, mock_model, mock_agent_cls): + agent_instance = MagicMock() + agent_instance.return_value = "Agent says hello" + mock_agent_cls.return_value = agent_instance + mock_mcp_client = MagicMock() + mock_mcp.return_value = mock_mcp_client + + from agent.agent_processor import process_request + result = process_request(_make_request(), gateway_url="https://gw.example.com") + + assert result.success is True + assert result.response == "Agent says hello" + mock_mcp.assert_called_once_with("https://gw.example.com", "jwt-tok") + mock_mcp_client.cleanup.assert_called_once() + + @patch("agent.agent_processor.Agent") + @patch("agent.agent_processor.create_bedrock_model") + @patch("agent.agent_processor.create_mcp_client") + def test_cleanup_called_on_agent_exception(self, mock_mcp, mock_model, mock_agent_cls): + """Verify MCP client cleanup happens even when the agent raises.""" + agent_instance = MagicMock() + agent_instance.side_effect = RuntimeError("LLM failed") + mock_agent_cls.return_value = agent_instance + mock_mcp_client = MagicMock() + mock_mcp.return_value = mock_mcp_client + + from agent.agent_processor import process_request + result = process_request(_make_request(), gateway_url="https://gw.example.com") + + assert result.success is False + assert result.error is not None + # The critical assertion: cleanup MUST be called even on failure + mock_mcp_client.cleanup.assert_called_once() + + @patch("agent.agent_processor.Agent") + @patch("agent.agent_processor.create_bedrock_model") + @patch("agent.agent_processor.create_mcp_client") + def test_cleanup_called_on_model_creation_exception(self, mock_mcp, mock_model, mock_agent_cls): + """Verify cleanup even if create_bedrock_model raises.""" + mock_model.side_effect = RuntimeError("Bedrock config error") + mock_mcp_client = MagicMock() + mock_mcp.return_value = mock_mcp_client + + from agent.agent_processor import process_request + result = process_request(_make_request(), gateway_url="https://gw.example.com") + + assert result.success is False + mock_mcp_client.cleanup.assert_called_once() + + def test_missing_gateway_url_returns_error(self): + """When no gateway URL is provided and env var is unset, return error.""" + from agent.agent_processor import process_request + with patch.dict("os.environ", {}, clear=True): + result = process_request(_make_request()) + + assert result.success is False + assert "gateway" in result.error.lower() + + @patch("agent.agent_processor.Agent") + @patch("agent.agent_processor.create_bedrock_model") + @patch("agent.agent_processor.create_mcp_client") + def test_reads_gateway_url_from_env(self, mock_mcp, mock_model, mock_agent_cls): + """Falls back to GATEWAY_URL env var when no explicit URL given.""" + agent_instance = MagicMock() + agent_instance.return_value = "ok" + mock_agent_cls.return_value = agent_instance + mock_mcp_client = MagicMock() + mock_mcp.return_value = mock_mcp_client + + from agent.agent_processor import process_request + with patch.dict("os.environ", {"GATEWAY_URL": "https://env-gw.example.com"}): + result = process_request(_make_request()) + + assert result.success is True + mock_mcp.assert_called_once_with("https://env-gw.example.com", "jwt-tok") + mock_mcp_client.cleanup.assert_called_once() + + @patch("agent.agent_processor.Agent") + @patch("agent.agent_processor.create_bedrock_model") + @patch("agent.agent_processor.create_mcp_client") + def test_explicit_url_overrides_env(self, mock_mcp, mock_model, mock_agent_cls): + agent_instance = MagicMock() + agent_instance.return_value = "ok" + mock_agent_cls.return_value = agent_instance + mock_mcp_client = MagicMock() + mock_mcp.return_value = mock_mcp_client + + from agent.agent_processor import process_request + with patch.dict("os.environ", {"GATEWAY_URL": "https://env-gw.example.com"}): + result = process_request(_make_request(), gateway_url="https://explicit.example.com") + + mock_mcp.assert_called_once_with("https://explicit.example.com", "jwt-tok") diff --git a/strands-agentcore-smithy/tests/unit/test_cloudformation_template.py b/strands-agentcore-smithy/tests/unit/test_cloudformation_template.py new file mode 100644 index 0000000000..883d26b10f --- /dev/null +++ b/strands-agentcore-smithy/tests/unit/test_cloudformation_template.py @@ -0,0 +1,363 @@ +"""Unit tests for CloudFormation template structure. + +Requirements: 1.1–1.9, 2.1–2.7, 3.1–3.6, 4.1–4.2, 5.1–5.2, 5.6–5.7, 7.1–7.6 +""" + +import json +import os + +import yaml + + +# --------------------------------------------------------------------------- +# Custom YAML loader for CloudFormation intrinsic functions +# --------------------------------------------------------------------------- + +class _CfnLoader(yaml.SafeLoader): + pass + + +def _cfn_tag_constructor(loader, tag_suffix, node): + if isinstance(node, yaml.ScalarNode): + return loader.construct_scalar(node) + if isinstance(node, yaml.SequenceNode): + return loader.construct_sequence(node) + if isinstance(node, yaml.MappingNode): + return loader.construct_mapping(node) + return None + + +_CfnLoader.add_multi_constructor("!", _cfn_tag_constructor) + +# --------------------------------------------------------------------------- +# Load template once +# --------------------------------------------------------------------------- + +_TEMPLATE_PATH = os.path.join( + os.path.dirname(__file__), "..", "..", "infrastructure", "template.yaml" +) + +with open(_TEMPLATE_PATH) as _fh: + TEMPLATE = yaml.load(_fh, Loader=_CfnLoader) + +RESOURCES = TEMPLATE["Resources"] +OUTPUTS = TEMPLATE.get("Outputs", {}) + +# Parse Smithy model from the official AWS model file (downloaded by deploy script) +_SMITHY_MODEL_PATH = os.path.join( + os.path.dirname(__file__), "..", "..", "infrastructure", "bedrock-runtime-2023-09-30.json" +) +# Fall back to /tmp if not in infrastructure/ +if not os.path.exists(_SMITHY_MODEL_PATH): + _SMITHY_MODEL_PATH = "/tmp/bedrock-runtime-2023-09-30.json" +with open(_SMITHY_MODEL_PATH) as _smithy_fh: + _SMITHY_PAYLOAD = _smithy_fh.read() +SMITHY_MODEL = json.loads(_SMITHY_PAYLOAD) + + +# =========================================================================== +# Gateway configuration tests (Requirements 2.1–2.7) +# =========================================================================== + +class TestGatewayConfiguration: + """Verify AgentCore Gateway resource configuration.""" + + def setup_method(self): + self.gateway = RESOURCES["AgentCoreGateway"]["Properties"] + + def test_protocol_type_is_mcp(self): + """Req 2.1: ProtocolType must be MCP.""" + assert self.gateway["ProtocolType"] == "MCP" + + def test_authorizer_type_is_custom_jwt(self): + """Req 2.2: AuthorizerType must be CUSTOM_JWT.""" + assert self.gateway["AuthorizerType"] == "CUSTOM_JWT" + + def test_custom_jwt_authorizer_key_casing(self): + """Req 2.2: Config block must be nested under AuthorizerConfiguration.CustomJWTAuthorizer.""" + assert "AuthorizerConfiguration" in self.gateway + auth_config = self.gateway["AuthorizerConfiguration"] + assert "CustomJWTAuthorizer" in auth_config + + def test_discovery_url_suffix(self): + """Req 2.3: DiscoveryUrl must end with /.well-known/openid-configuration.""" + url = self.gateway["AuthorizerConfiguration"]["CustomJWTAuthorizer"]["DiscoveryUrl"] + assert url.endswith("/.well-known/openid-configuration") + + def test_allowed_audience_not_audience(self): + """Req 2.4: Must use AllowedAudience, not Audience.""" + authorizer = self.gateway["AuthorizerConfiguration"]["CustomJWTAuthorizer"] + assert "AllowedAudience" in authorizer + assert "Audience" not in authorizer + + def test_role_arn_not_execution_role_arn(self): + """Req 2.5: Must use RoleArn, not ExecutionRoleArn.""" + assert "RoleArn" in self.gateway + assert "ExecutionRoleArn" not in self.gateway + + def test_gateway_target_credential_provider_type(self): + """Req 2.7: GatewayTarget must use GATEWAY_IAM_ROLE.""" + target = RESOURCES["SmithyTarget"]["Properties"] + cred_configs = target["CredentialProviderConfigurations"] + assert isinstance(cred_configs, list) + assert len(cred_configs) >= 1 + assert cred_configs[0]["CredentialProviderType"] == "GATEWAY_IAM_ROLE" + + def test_target_config_nesting(self): + """Req 2.6: TargetConfiguration nested as Mcp.SmithyModel with S3.""" + target_config = RESOURCES["SmithyTarget"]["Properties"]["TargetConfiguration"] + assert "Mcp" in target_config + assert "SmithyModel" in target_config["Mcp"] + assert "S3" in target_config["Mcp"]["SmithyModel"] + + +# =========================================================================== +# Gateway Execution Role tests (Requirements 3.1–3.6) +# =========================================================================== + +class TestGatewayExecutionRole: + """Verify GatewayExecutionRole IAM configuration.""" + + def setup_method(self): + self.role = RESOURCES["GatewayExecutionRole"]["Properties"] + # Collect all policy statements across all policies + self.all_statements = [] + for policy in self.role["Policies"]: + stmts = policy["PolicyDocument"]["Statement"] + self.all_statements.extend(stmts) + # Flatten all resource ARNs + self.all_resources = [] + for stmt in self.all_statements: + res = stmt.get("Resource", []) + if isinstance(res, str): + self.all_resources.append(res) + elif isinstance(res, list): + self.all_resources.extend(res) + # Flatten all actions + self.all_actions = [] + for stmt in self.all_statements: + actions = stmt.get("Action", []) + if isinstance(actions, str): + self.all_actions.append(actions) + elif isinstance(actions, list): + self.all_actions.extend(actions) + + def test_trust_policy_principal(self): + """Trust policy must allow bedrock-agentcore.amazonaws.com.""" + trust = self.role["AssumeRolePolicyDocument"] + principals = [] + for stmt in trust["Statement"]: + svc = stmt.get("Principal", {}).get("Service", "") + if isinstance(svc, str): + principals.append(svc) + elif isinstance(svc, list): + principals.extend(svc) + assert "bedrock-agentcore.amazonaws.com" in principals + + def test_token_vault_default_arn(self): + """Req 3.1: Must include token-vault/default ARN pattern.""" + assert any("token-vault/default" in r and "apikeycredentialprovider" not in r + for r in self.all_resources) + + def test_apikeycredentialprovider_wildcard_arn(self): + """Req 3.2: Must include apikeycredentialprovider/* ARN pattern.""" + assert any("apikeycredentialprovider/*" in r for r in self.all_resources) + + def test_workload_identity_directory_default_arn(self): + """Req 3.3: Must include workload-identity-directory/default ARN.""" + assert any( + "workload-identity-directory/default" in r + and "workload-identity" not in r.split("workload-identity-directory/default")[1][:1] + or r.endswith("workload-identity-directory/default") + for r in self.all_resources + ) + + def test_workload_identity_wildcard_arn(self): + """Req 3.4: Must include workload-identity-directory/default/workload-identity/{gateway}-* ARN.""" + assert any( + "workload-identity-directory/default/workload-identity/" in r + for r in self.all_resources + ) + + def test_bedrock_runtime_permissions(self): + """Req 3.5: Must include bedrock:InvokeModel.""" + required_actions = {"bedrock:InvokeModel"} + assert required_actions.issubset(set(self.all_actions)) + + def test_no_apigateway_get_permission(self): + """Req 3.6: Must NOT include apigateway:GET.""" + for action in self.all_actions: + assert "apigateway" not in action.lower() + + +# =========================================================================== +# Smithy model tests (Requirements 1.1–1.9) +# =========================================================================== + +class TestSmithyModel: + """Verify inline Smithy model structure.""" + + def setup_method(self): + self.shapes = SMITHY_MODEL["shapes"] + # Find service shape + self.service = None + for key, shape in self.shapes.items(): + if shape.get("type") == "service": + self.service = shape + self.service_key = key + break + + def test_smithy_version(self): + """Smithy model must be version 2.0.""" + assert SMITHY_MODEL["smithy"] == "2.0" + + def test_service_has_restjson1_trait(self): + """Req 1.1: Service must have aws.protocols#restJson1 trait.""" + assert "aws.protocols#restJson1" in self.service.get("traits", {}) + + def test_service_version(self): + """Service must have a version string.""" + assert self.service["version"] is not None + assert len(self.service["version"]) > 0 + + def test_defines_invoke_model_operation(self): + """Must define InvokeModel with POST method.""" + op = self.shapes["com.amazonaws.bedrockruntime#InvokeModel"] + http = op["traits"]["smithy.api#http"] + assert http["method"] == "POST" + assert "/model/" in http["uri"] + + def test_has_operations(self): + """Service must define operations (via resources).""" + resources = self.service.get("resources", []) + assert len(resources) > 0 + + def test_model_under_10mb(self): + """Req 1.8: Smithy model must be under 10MB.""" + assert len(_SMITHY_PAYLOAD.encode("utf-8")) < 10 * 1024 * 1024 + + +# =========================================================================== +# Lambda configuration tests (Requirements 5.1, 5.2, 5.6, 5.7) +# =========================================================================== + +class TestLambdaConfiguration: + """Verify Agent Lambda Function resource configuration.""" + + def setup_method(self): + self.lambda_props = RESOURCES["AgentLambdaFunction"]["Properties"] + self.lambda_role = RESOURCES["AgentLambdaRole"]["Properties"] + # Collect all actions from Lambda role policies + self.all_actions = [] + for policy in self.lambda_role["Policies"]: + for stmt in policy["PolicyDocument"]["Statement"]: + actions = stmt.get("Action", []) + if isinstance(actions, str): + self.all_actions.append(actions) + elif isinstance(actions, list): + self.all_actions.extend(actions) + + def test_runtime_python312(self): + """Req 5.1: Runtime must be python3.12.""" + assert self.lambda_props["Runtime"] == "python3.12" + + def test_architecture_x86_64(self): + """Req 5.1: Architecture must be x86_64.""" + assert "x86_64" in self.lambda_props["Architectures"] + + def test_timeout_at_least_120(self): + """Req 5.2: Timeout must be >= 120 seconds.""" + assert self.lambda_props["Timeout"] >= 120 + + def test_memory_at_least_1024(self): + """Req 5.2: MemorySize must be >= 1024 MB.""" + assert self.lambda_props["MemorySize"] >= 1024 + + def test_bedrock_invoke_permissions(self): + """Req 5.6: Must have bedrock:InvokeModel and related permissions.""" + required = { + "bedrock:InvokeModel", + "bedrock:InvokeModelWithResponseStream", + "bedrock:Converse", + "bedrock:ConverseStream", + } + assert required.issubset(set(self.all_actions)) + + def test_agentcore_invoke_gateway_permission(self): + """Req 5.7: Must have bedrock-agentcore:InvokeGateway permission.""" + assert "bedrock-agentcore:InvokeGateway" in self.all_actions + + def test_cloudwatch_logs_permissions(self): + """Lambda role must have CloudWatch Logs permissions.""" + required = {"logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents"} + assert required.issubset(set(self.all_actions)) + + +# =========================================================================== +# Cognito configuration tests (Requirements 4.1, 4.2) +# =========================================================================== + +class TestCognitoConfiguration: + """Verify Cognito User Pool and Client configuration.""" + + def test_user_pool_exists(self): + """Req 4.1: CognitoUserPool resource must exist.""" + assert "CognitoUserPool" in RESOURCES + assert RESOURCES["CognitoUserPool"]["Type"] == "AWS::Cognito::UserPool" + + def test_user_pool_client_exists(self): + """Req 4.2: CognitoUserPoolClient resource must exist.""" + assert "CognitoUserPoolClient" in RESOURCES + assert RESOURCES["CognitoUserPoolClient"]["Type"] == "AWS::Cognito::UserPoolClient" + + def test_user_password_auth_flow(self): + """Req 4.1: Must include USER_PASSWORD_AUTH explicit auth flow.""" + client_props = RESOURCES["CognitoUserPoolClient"]["Properties"] + assert "USER_PASSWORD_AUTH" in client_props["ExplicitAuthFlows"] + + +# =========================================================================== +# No forbidden resources (Requirements 7.4, 7.5) +# =========================================================================== + +class TestNoForbiddenResources: + """Verify template does not contain forbidden resource types.""" + + def test_no_api_gateway_resources(self): + """Req 7.4: No AWS::ApiGateway::* or AWS::ApiGatewayV2::* resources.""" + for name, res in RESOURCES.items(): + rtype = res.get("Type", "") + assert not rtype.startswith("AWS::ApiGateway::"), ( + f"Found API Gateway v1 resource: {name}" + ) + assert not rtype.startswith("AWS::ApiGatewayV2::"), ( + f"Found API Gateway v2 resource: {name}" + ) + + def test_no_secrets_manager_resources(self): + """Req 7.5: No AWS::SecretsManager::Secret resources.""" + for name, res in RESOURCES.items(): + rtype = res.get("Type", "") + assert not rtype.startswith("AWS::SecretsManager::"), ( + f"Found Secrets Manager resource: {name}" + ) + + +# =========================================================================== +# Stack outputs (Requirement 7.6) +# =========================================================================== + +class TestStackOutputs: + """Verify required stack outputs are defined.""" + + def test_gateway_id_output(self): + assert "GatewayId" in OUTPUTS + + def test_cognito_user_pool_id_output(self): + assert "CognitoUserPoolId" in OUTPUTS + + def test_cognito_client_id_output(self): + assert "CognitoClientId" in OUTPUTS + + def test_lambda_function_name_output(self): + assert "LambdaFunctionName" in OUTPUTS diff --git a/strands-agentcore-smithy/tests/unit/test_deploy_script.py b/strands-agentcore-smithy/tests/unit/test_deploy_script.py new file mode 100644 index 0000000000..53ccaf8b76 --- /dev/null +++ b/strands-agentcore-smithy/tests/unit/test_deploy_script.py @@ -0,0 +1,192 @@ +"""Unit tests for the SAM deploy script (scripts/deploy.sh) and Makefile build. + +After the migration from raw CloudFormation to AWS SAM: + - Orchestration (validate / build / deploy) lives in scripts/deploy.sh and + uses the `sam` CLI. + - Lambda packaging (the two-step pip3 install) lives in the root Makefile, + invoked by `sam build` via BuildMethod: makefile (no Docker required). +""" + +import os +import re + +_SCRIPT_PATH = os.path.join( + os.path.dirname(__file__), "..", "..", "scripts", "deploy.sh" +) +_MAKEFILE_PATH = os.path.join( + os.path.dirname(__file__), "..", "..", "Makefile" +) + +with open(_SCRIPT_PATH) as _fh: + DEPLOY_CONTENT = _fh.read() + +with open(_MAKEFILE_PATH) as _fh: + MAKEFILE_CONTENT = _fh.read() + + +# ── Makefile: two-step pip3 install (packaging moved out of deploy.sh) ── + +class TestTwoStepPipInstall: + """Verify the two-step pip3 install pattern lives in the Makefile.""" + + def test_first_step_has_only_binary(self): + """First pip3 install uses --only-binary=:all:""" + first_pip = re.search( + r"pip3\s+install\b.*?--only-binary=:all:", MAKEFILE_CONTENT, re.DOTALL + ) + assert first_pip is not None, "First pip3 install must use --only-binary=:all:" + + def test_first_step_has_platform_targeting(self): + """First pip3 install targets manylinux2014_x86_64.""" + assert re.search( + r"pip3\s+install\b.*--platform\s+manylinux2014_x86_64", MAKEFILE_CONTENT, re.DOTALL + ), "First pip3 install must target --platform manylinux2014_x86_64" + + def test_first_step_has_python_version(self): + """First pip3 install specifies --python-version 3.12.""" + assert re.search( + r"pip3\s+install\b.*--python-version\s+3\.12", MAKEFILE_CONTENT, re.DOTALL + ), "First pip3 install must specify --python-version 3.12" + + def test_second_step_has_no_deps(self): + """Second pip3 install uses --no-deps for pure Python packages.""" + no_deps_match = re.search( + r"pip3\s+install\b.*?--no-deps", MAKEFILE_CONTENT, re.DOTALL + ) + assert no_deps_match is not None, "Second pip3 install must use --no-deps" + + def test_second_step_includes_pure_python_packages(self): + """Second step installs the expected pure Python packages.""" + no_deps_section = re.search( + r"pip3\s+install\b.*?--no-deps\b(.*)", MAKEFILE_CONTENT, re.DOTALL + ) + assert no_deps_section is not None + block = no_deps_section.group(0) + for pkg in ["requests", "urllib3", "charset-normalizer", "idna", "certifi", "PyJWT", "cryptography", "cffi"]: + assert pkg in block, f"Second pip3 install must include {pkg}" + + def test_at_least_two_pip3_install_commands(self): + """There should be at least two pip3 install invocations (binary + pure Python).""" + invocations = re.findall(r"\bpip3\s+install\b", MAKEFILE_CONTENT) + assert len(invocations) >= 2, f"Expected at least 2 pip3 install commands, found {len(invocations)}" + + +# ── No .dist-info removal ── + +class TestNoDistInfoRemoval: + def test_no_dist_info_removal(self): + """Neither the Makefile nor deploy.sh must remove .dist-info directories.""" + combined = MAKEFILE_CONTENT + "\n" + DEPLOY_CONTENT + assert not re.search(r"rm\s.*\.dist-info", combined), ( + "Must not remove .dist-info directories" + ) + assert not re.search(r"find.*\.dist-info.*-delete", combined), ( + "Must not delete .dist-info via find" + ) + + +# ── SAM build handles packaging + artifact upload ── + +class TestSamBuildAndDeploy: + def test_uses_sam_build(self): + """Script builds the Lambda with `sam build`.""" + assert re.search(r"\bsam\s+build\b", DEPLOY_CONTENT), "Script must call sam build" + + def test_uses_sam_deploy(self): + """Script deploys with `sam deploy`.""" + assert re.search(r"\bsam\s+deploy\b", DEPLOY_CONTENT), "Script must call sam deploy" + + def test_sam_deploy_has_named_iam_capability(self): + """SAM deploy must pass CAPABILITY_NAMED_IAM for the named roles.""" + assert "CAPABILITY_NAMED_IAM" in DEPLOY_CONTENT + + def test_sam_deploy_resolves_artifact_bucket(self): + """SAM deploy manages the artifact bucket via --resolve-s3.""" + assert "--resolve-s3" in DEPLOY_CONTENT + + def test_makefile_build_method(self): + """The Makefile exposes the SAM build target for the function.""" + assert "build-AgentLambdaFunction" in MAKEFILE_CONTENT + + +# ── Required dependencies ── + +class TestRequiredDependencies: + def test_requirements_txt_referenced(self): + """Makefile references requirements.txt for pip install.""" + assert "requirements.txt" in MAKEFILE_CONTENT + + +# ── Template validation before deployment ── + +class TestTemplateValidation: + def test_validate_template_before_deploy(self): + """Script validates the SAM template before deploying.""" + validate_pos = DEPLOY_CONTENT.find("sam validate") + deploy_pos = DEPLOY_CONTENT.find("sam deploy") + assert validate_pos != -1, "Script must call sam validate" + assert deploy_pos != -1, "Script must call sam deploy" + assert validate_pos < deploy_pos, "sam validate must come before sam deploy" + + def test_references_sam_template(self): + """Script references the SAM template path.""" + assert "infrastructure/template.yaml" in DEPLOY_CONTENT + + +# ── Smithy model upload (must precede stack deploy) ── + +class TestSmithyModelUpload: + def test_uploads_smithy_model_to_s3(self): + """Script uploads the Smithy model with `aws s3 cp`.""" + assert re.search(r"aws\s+s3\s+cp\b", DEPLOY_CONTENT), ( + "Script must upload the Smithy model with aws s3 cp" + ) + + def test_smithy_upload_before_deploy(self): + """Smithy model must be in S3 before the stack (and its target) deploys.""" + upload_pos = DEPLOY_CONTENT.find("aws s3 cp") + deploy_pos = DEPLOY_CONTENT.find("sam deploy") + assert upload_pos != -1 and deploy_pos != -1 + assert upload_pos < deploy_pos, "Smithy upload must precede sam deploy" + + +# ── Cognito test user creation ── + +class TestCognitoUser: + def test_creates_cognito_user(self): + """Script creates a Cognito test user.""" + assert "admin-create-user" in DEPLOY_CONTENT + assert "admin-set-user-password" in DEPLOY_CONTENT + + +# ── pip3 exclusively (in the Makefile) ── + +class TestPip3Exclusively: + def test_all_pip_commands_use_pip3(self): + """All pip install commands use pip3, not bare pip.""" + invocations = re.findall(r"\b(pip3?)\s+install\b", MAKEFILE_CONTENT) + assert len(invocations) > 0, "Must have pip install commands" + for inv in invocations: + assert inv == "pip3", f"Found bare '{inv}' instead of 'pip3'" + + +# ── No credential provider commands ── + +class TestNoForbiddenPatterns: + def test_no_credential_provider_commands(self): + """Script must not contain credential provider CLI commands.""" + forbidden = [ + "create-credential-provider", + "create-api-key-credential-provider", + "get-api-key", + ] + for pattern in forbidden: + assert pattern not in DEPLOY_CONTENT, ( + f"Script must not contain '{pattern}'" + ) + + def test_no_api_key_retrieval(self): + """Script must not retrieve API keys.""" + assert not re.search(r"get-api-key", DEPLOY_CONTENT), ( + "Script must not retrieve API keys" + ) diff --git a/strands-agentcore-smithy/tests/unit/test_error_utils.py b/strands-agentcore-smithy/tests/unit/test_error_utils.py new file mode 100644 index 0000000000..2b51210282 --- /dev/null +++ b/strands-agentcore-smithy/tests/unit/test_error_utils.py @@ -0,0 +1,60 @@ +"""Unit tests for error_utils module. + +Requirements: 11.3 +""" + +import logging + +from shared.error_utils import ( + format_error_response, + format_unauthorized_response, + format_internal_error_response, +) + + +class TestFormatErrorResponse: + def test_returns_failed_response(self): + resp = format_error_response("something broke") + assert resp.success is False + assert resp.response == "" + assert resp.error == "something broke" + + def test_logs_server_error_at_error_level(self, caplog): + with caplog.at_level(logging.ERROR): + format_error_response("server fail", status_code=500) + assert "server fail" in caplog.text + + def test_logs_client_error_at_warning_level(self, caplog): + with caplog.at_level(logging.WARNING): + format_error_response("bad request", status_code=400) + assert "bad request" in caplog.text + + def test_custom_log_message(self, caplog): + with caplog.at_level(logging.ERROR): + format_error_response("user msg", status_code=500, log_message="internal detail") + assert "internal detail" in caplog.text + + +class TestFormatUnauthorizedResponse: + def test_default_message(self): + resp = format_unauthorized_response() + assert resp.success is False + assert resp.error == "Unauthorized" + + def test_custom_detail(self): + resp = format_unauthorized_response("Token expired") + assert resp.error == "Token expired" + + +class TestFormatInternalErrorResponse: + def test_default_message(self): + resp = format_internal_error_response() + assert resp.success is False + assert resp.error == "Internal server error" + + def test_with_exception(self, caplog): + exc = ValueError("division by zero") + with caplog.at_level(logging.ERROR): + resp = format_internal_error_response("calc failed", exception=exc) + assert resp.error == "calc failed" + assert "division by zero" in caplog.text diff --git a/strands-agentcore-smithy/tests/unit/test_handler.py b/strands-agentcore-smithy/tests/unit/test_handler.py new file mode 100644 index 0000000000..a7521abd69 --- /dev/null +++ b/strands-agentcore-smithy/tests/unit/test_handler.py @@ -0,0 +1,131 @@ +"""Unit tests for the agent Lambda handler. + +Tests JWT extraction from events, validation error responses, +missing prompt handling, and successful delegation to agent_processor. + +Requirements: 5.1, 5.5, 5.6 +""" + +import sys +from types import ModuleType +from unittest.mock import patch, MagicMock + +import pytest + +# Mock third-party modules not installed in test env +_strands_mod = ModuleType("strands") +_strands_mod.Agent = MagicMock() +_mcp_mod = ModuleType("mcp") +_mcp_client_mod = ModuleType("mcp.client") +_mcp_http_mod = ModuleType("mcp.client.streamable_http") +_mcp_http_mod.streamablehttp_client = MagicMock() +_bedrock_mod = ModuleType("strands.models") +_bedrock_bedrock_mod = ModuleType("strands.models.bedrock") +_bedrock_bedrock_mod.BedrockModel = MagicMock() +_strands_tools_mod = ModuleType("strands.tools") +_strands_tools_mcp_mod = ModuleType("strands.tools.mcp") +_strands_tools_mcp_mod.MCPClient = MagicMock() + +sys.modules.setdefault("strands", _strands_mod) +sys.modules.setdefault("strands.models", _bedrock_mod) +sys.modules.setdefault("strands.models.bedrock", _bedrock_bedrock_mod) +sys.modules.setdefault("strands.tools", _strands_tools_mod) +sys.modules.setdefault("strands.tools.mcp", _strands_tools_mcp_mod) +sys.modules.setdefault("mcp", _mcp_mod) +sys.modules.setdefault("mcp.client", _mcp_client_mod) +sys.modules.setdefault("mcp.client.streamable_http", _mcp_http_mod) + +from agent.handler import lambda_handler, _extract_token +from shared.models import AgentResponse + + +# --- _extract_token --- + +class TestExtractToken: + def test_extracts_direct_token(self): + event = {"token": "my-jwt"} + assert _extract_token(event) == "my-jwt" + + def test_extracts_bearer_token_from_headers(self): + event = {"headers": {"Authorization": "Bearer abc123"}} + assert _extract_token(event) == "abc123" + + def test_direct_token_takes_precedence_over_header(self): + event = {"token": "direct", "headers": {"Authorization": "Bearer header"}} + assert _extract_token(event) == "direct" + + def test_returns_raw_auth_header_if_not_bearer(self): + event = {"headers": {"Authorization": "Basic creds"}} + assert _extract_token(event) == "Basic creds" + + def test_returns_none_when_no_token(self): + assert _extract_token({}) is None + + def test_returns_none_for_empty_auth_header(self): + event = {"headers": {"Authorization": ""}} + assert _extract_token(event) is None + + def test_returns_none_for_missing_headers(self): + event = {"headers": {}} + assert _extract_token(event) is None + + +# --- lambda_handler --- + +class TestLambdaHandler: + """Tests for the Lambda entry point.""" + + def test_missing_token_returns_unauthorized(self): + result = lambda_handler({}, None) + assert result["success"] is False + assert "authentication" in result["error"].lower() or "token" in result["error"].lower() + + @patch("agent.handler.validate_token") + def test_invalid_jwt_returns_unauthorized(self, mock_validate): + from src.shared.jwt_utils import JWTValidationError + mock_validate.side_effect = JWTValidationError("Token expired") + + result = lambda_handler({"token": "bad-jwt"}, None) + assert result["success"] is False + assert "expired" in result["error"].lower() + + @patch("agent.handler.extract_username") + @patch("agent.handler.validate_token") + def test_missing_prompt_returns_error(self, mock_validate, mock_extract): + mock_validate.return_value = {"cognito:username": "alice"} + mock_extract.return_value = "alice" + + result = lambda_handler({"token": "valid-jwt"}, None) + assert result["success"] is False + assert "prompt" in result["error"].lower() + + @patch("agent.handler.process_request") + @patch("agent.handler.extract_username") + @patch("agent.handler.validate_token") + def test_successful_request(self, mock_validate, mock_extract, mock_process): + mock_validate.return_value = {"cognito:username": "alice"} + mock_extract.return_value = "alice" + mock_process.return_value = AgentResponse(success=True, response="done") + + result = lambda_handler({"token": "jwt", "prompt": "hello"}, None) + assert result["success"] is True + assert result["response"] == "done" + assert result["error"] is None + + # Verify process_request was called with correct AgentRequest + call_args = mock_process.call_args[0][0] + assert call_args.prompt == "hello" + assert call_args.user_context.username == "alice" + assert call_args.user_context.token == "jwt" + + @patch("agent.handler.process_request") + @patch("agent.handler.extract_username") + @patch("agent.handler.validate_token") + def test_processor_exception_returns_internal_error(self, mock_validate, mock_extract, mock_process): + mock_validate.return_value = {"cognito:username": "alice"} + mock_extract.return_value = "alice" + mock_process.side_effect = RuntimeError("boom") + + result = lambda_handler({"token": "jwt", "prompt": "hello"}, None) + assert result["success"] is False + assert result["error"] is not None diff --git a/strands-agentcore-smithy/tests/unit/test_jwt_utils.py b/strands-agentcore-smithy/tests/unit/test_jwt_utils.py new file mode 100644 index 0000000000..9ae9f00df6 --- /dev/null +++ b/strands-agentcore-smithy/tests/unit/test_jwt_utils.py @@ -0,0 +1,98 @@ +"""Unit tests for JWT validation edge cases. + +Requirements: 4.4, 4.5, 4.6 +""" + +import time + +import jwt as pyjwt +import pytest + +from shared.jwt_utils import validate_token, extract_username, JWTValidationError + + +def _encode(claims: dict, exp_offset: int = 3600) -> str: + payload = { + "iat": int(time.time()), + "exp": int(time.time()) + exp_offset, + **claims, + } + return pyjwt.encode(payload, "test-secret", algorithm="HS256") + + +# --- validate_token --- + +class TestValidateToken: + def test_accepts_token_use_access(self): + token = _encode({"token_use": "access"}) + claims = validate_token(token) + assert claims["token_use"] == "access" + + def test_accepts_token_use_id(self): + token = _encode({"token_use": "id"}) + claims = validate_token(token) + assert claims["token_use"] == "id" + + def test_accepts_missing_token_use(self): + """Tokens without token_use claim should still be accepted.""" + token = _encode({"sub": "user1"}) + claims = validate_token(token) + assert "token_use" not in claims + + def test_rejects_invalid_token_use(self): + token = _encode({"token_use": "refresh"}) + with pytest.raises(JWTValidationError, match="Unsupported token_use"): + validate_token(token) + + def test_rejects_empty_token(self): + with pytest.raises(JWTValidationError, match="empty or missing"): + validate_token("") + + def test_rejects_none_like_empty(self): + with pytest.raises(JWTValidationError): + validate_token("") + + def test_rejects_malformed_token(self): + with pytest.raises(JWTValidationError, match="Invalid token"): + validate_token("not.a.jwt.at.all") + + def test_rejects_expired_token(self): + token = _encode({"token_use": "id"}, exp_offset=-10) + with pytest.raises(JWTValidationError, match="expired"): + validate_token(token) + + def test_verify_aud_disabled_by_default(self): + """Tokens with an aud claim should not fail since verify_aud is False.""" + token = _encode({"token_use": "id", "aud": "some-client-id"}) + claims = validate_token(token) + assert claims["aud"] == "some-client-id" + + +# --- extract_username --- + +class TestExtractUsername: + def test_extracts_cognito_username(self): + claims = {"cognito:username": "alice"} + assert extract_username(claims) == "alice" + + def test_ignores_username_claim(self): + claims = {"cognito:username": "alice", "username": "bob"} + assert extract_username(claims) == "alice" + + def test_ignores_sub_claim(self): + claims = {"cognito:username": "alice", "sub": "sub-123"} + assert extract_username(claims) == "alice" + + def test_raises_when_cognito_username_missing(self): + claims = {"username": "bob", "sub": "sub-123"} + with pytest.raises(JWTValidationError, match="cognito:username"): + extract_username(claims) + + def test_raises_on_empty_claims(self): + with pytest.raises(JWTValidationError, match="cognito:username"): + extract_username({}) + + def test_raises_on_empty_string_cognito_username(self): + claims = {"cognito:username": ""} + with pytest.raises(JWTValidationError, match="cognito:username"): + extract_username(claims) diff --git a/strands-agentcore-smithy/tests/unit/test_logging_utils.py b/strands-agentcore-smithy/tests/unit/test_logging_utils.py new file mode 100644 index 0000000000..3e0be82327 --- /dev/null +++ b/strands-agentcore-smithy/tests/unit/test_logging_utils.py @@ -0,0 +1,56 @@ +"""Unit tests for logging_utils module. + +Requirements: 11.3 +""" + +import logging +import uuid + +from shared.logging_utils import get_correlation_id, configure_logging, get_logger + + +class TestGetCorrelationId: + def test_extracts_request_id_from_event(self): + event = {"requestContext": {"requestId": "req-abc-123"}} + assert get_correlation_id(event) == "req-abc-123" + + def test_generates_uuid_when_no_event(self): + cid = get_correlation_id(None) + # Should be a valid UUID + uuid.UUID(cid) + + def test_generates_uuid_when_event_has_no_request_context(self): + cid = get_correlation_id({"body": "hello"}) + uuid.UUID(cid) + + def test_generates_uuid_when_request_id_missing(self): + event = {"requestContext": {}} + cid = get_correlation_id(event) + uuid.UUID(cid) + + def test_generates_uuid_for_empty_event(self): + cid = get_correlation_id({}) + uuid.UUID(cid) + + +class TestConfigureLogging: + def test_returns_root_logger(self): + logger = configure_logging() + assert isinstance(logger, logging.Logger) + + def test_sets_log_level(self): + logger = configure_logging(level=logging.DEBUG) + assert logger.level == logging.DEBUG + + def test_includes_correlation_id_in_format(self): + logger = configure_logging(correlation_id="corr-xyz") + handler = logger.handlers[-1] if logger.handlers else None + if handler: + assert "corr-xyz" in handler.formatter._fmt + + +class TestGetLogger: + def test_returns_named_logger(self): + logger = get_logger("my.module") + assert logger.name == "my.module" + assert isinstance(logger, logging.Logger) diff --git a/strands-agentcore-smithy/tests/unit/test_models.py b/strands-agentcore-smithy/tests/unit/test_models.py new file mode 100644 index 0000000000..78cdfc6985 --- /dev/null +++ b/strands-agentcore-smithy/tests/unit/test_models.py @@ -0,0 +1,54 @@ +"""Unit tests for shared data models (UserContext, AgentRequest, AgentResponse). + +Requirements: 11.3 +""" + +from shared.models import UserContext, AgentRequest, AgentResponse + + +class TestUserContext: + def test_creation(self): + ctx = UserContext(username="alice", token="tok-123") + assert ctx.username == "alice" + assert ctx.token == "tok-123" + + def test_equality(self): + a = UserContext(username="bob", token="t") + b = UserContext(username="bob", token="t") + assert a == b + + def test_inequality(self): + a = UserContext(username="bob", token="t1") + b = UserContext(username="bob", token="t2") + assert a != b + + +class TestAgentRequest: + def test_creation(self): + ctx = UserContext(username="u", token="t") + req = AgentRequest(prompt="hello", user_context=ctx) + assert req.prompt == "hello" + assert req.user_context.username == "u" + + def test_equality(self): + ctx = UserContext(username="u", token="t") + a = AgentRequest(prompt="p", user_context=ctx) + b = AgentRequest(prompt="p", user_context=ctx) + assert a == b + + +class TestAgentResponse: + def test_success_response(self): + resp = AgentResponse(success=True, response="ok") + assert resp.success is True + assert resp.response == "ok" + assert resp.error is None + + def test_error_response(self): + resp = AgentResponse(success=False, response="", error="bad") + assert resp.success is False + assert resp.error == "bad" + + def test_error_defaults_to_none(self): + resp = AgentResponse(success=True, response="ok") + assert resp.error is None diff --git a/strands-agentcore-smithy/tests/unit/test_strands_client.py b/strands-agentcore-smithy/tests/unit/test_strands_client.py new file mode 100644 index 0000000000..8131119246 --- /dev/null +++ b/strands-agentcore-smithy/tests/unit/test_strands_client.py @@ -0,0 +1,115 @@ +"""Unit tests for the Strands client factory functions. + +Tests MCP client creation and Bedrock model configuration. + +Requirements: 5.3, 5.4 +""" + +import sys +from types import ModuleType +from unittest.mock import patch, MagicMock + +# Mock third-party modules not installed in test env +_strands_mod = ModuleType("strands") +_strands_mod.Agent = MagicMock() +_mcp_mod = ModuleType("mcp") +_mcp_client_mod = ModuleType("mcp.client") +_mcp_http_mod = ModuleType("mcp.client.streamable_http") +_mcp_http_mod.streamablehttp_client = MagicMock() +_bedrock_mod = ModuleType("strands.models") +_bedrock_bedrock_mod = ModuleType("strands.models.bedrock") +_bedrock_bedrock_mod.BedrockModel = MagicMock() +_strands_tools_mod = ModuleType("strands.tools") +_strands_tools_mcp_mod = ModuleType("strands.tools.mcp") +_strands_tools_mcp_mod.MCPClient = MagicMock() + +sys.modules.setdefault("strands", _strands_mod) +sys.modules.setdefault("strands.models", _bedrock_mod) +sys.modules.setdefault("strands.models.bedrock", _bedrock_bedrock_mod) +sys.modules.setdefault("strands.tools", _strands_tools_mod) +sys.modules.setdefault("strands.tools.mcp", _strands_tools_mcp_mod) +sys.modules.setdefault("mcp", _mcp_mod) +sys.modules.setdefault("mcp.client", _mcp_client_mod) +sys.modules.setdefault("mcp.client.streamable_http", _mcp_http_mod) + +from agent.strands_client import ( + create_mcp_client, + create_bedrock_model, + DEFAULT_MODEL_ID, + DEFAULT_REGION, +) + + +class TestCreateMCPClient: + """Tests for the MCP client factory.""" + + @patch("agent.strands_client.MCPClient") + def test_returns_mcp_client_instance(self, mock_mcp_cls): + client = create_mcp_client("https://gw.example.com", "my-token") + mock_mcp_cls.assert_called_once() + assert client is mock_mcp_cls.return_value + + @patch("agent.strands_client.streamablehttp_client") + @patch("agent.strands_client.MCPClient") + def test_transport_factory_uses_bearer_token(self, mock_mcp_cls, mock_http): + """The transport factory lambda should pass the Bearer token header.""" + create_mcp_client("https://gw.example.com", "tok-abc") + + # Extract the transport factory lambda passed to MCPClient + transport_factory = mock_mcp_cls.call_args[0][0] + transport_factory() + + mock_http.assert_called_once_with( + "https://gw.example.com", + headers={"Authorization": "Bearer tok-abc"}, + ) + + @patch("agent.strands_client.streamablehttp_client") + @patch("agent.strands_client.MCPClient") + def test_different_urls_and_tokens(self, mock_mcp_cls, mock_http): + """Verify URL and token are correctly threaded through.""" + create_mcp_client("https://other.example.com", "other-token") + transport_factory = mock_mcp_cls.call_args[0][0] + transport_factory() + + mock_http.assert_called_once_with( + "https://other.example.com", + headers={"Authorization": "Bearer other-token"}, + ) + + +class TestCreateBedrockModel: + """Tests for the Bedrock model factory.""" + + @patch("agent.strands_client.BedrockModel") + def test_default_model_and_region(self, mock_bedrock_cls): + model = create_bedrock_model() + mock_bedrock_cls.assert_called_once_with( + model_id=DEFAULT_MODEL_ID, + region_name=DEFAULT_REGION, + ) + assert model is mock_bedrock_cls.return_value + + @patch("agent.strands_client.BedrockModel") + def test_custom_model_id(self, mock_bedrock_cls): + create_bedrock_model(model_id="custom-model") + mock_bedrock_cls.assert_called_once_with( + model_id="custom-model", + region_name=DEFAULT_REGION, + ) + + @patch("agent.strands_client.BedrockModel") + def test_custom_region(self, mock_bedrock_cls): + create_bedrock_model(region_name="eu-west-1") + mock_bedrock_cls.assert_called_once_with( + model_id=DEFAULT_MODEL_ID, + region_name="eu-west-1", + ) + + @patch("agent.strands_client.BedrockModel") + def test_custom_model_and_region(self, mock_bedrock_cls): + create_bedrock_model(model_id="my-model", region_name="ap-southeast-1") + mock_bedrock_cls.assert_called_once_with( + model_id="my-model", + region_name="ap-southeast-1", + ) diff --git a/strands-agentcore-smithy/tests/unit/test_test_script.py b/strands-agentcore-smithy/tests/unit/test_test_script.py new file mode 100644 index 0000000000..fc85a8724f --- /dev/null +++ b/strands-agentcore-smithy/tests/unit/test_test_script.py @@ -0,0 +1,142 @@ +"""Unit tests for the generated test script pattern in deploy.sh. + +The test script (scripts/test.sh) is generated by deploy.sh at runtime. +These tests validate the heredoc template embedded in deploy.sh. + +Validates: Requirements 10.1–10.5 +""" + +import os +import re + +import pytest + +_DEPLOY_PATH = os.path.join( + os.path.dirname(__file__), "..", "..", "scripts", "deploy.sh" +) + +with open(_DEPLOY_PATH) as _fh: + DEPLOY_CONTENT = _fh.read() + +# Extract the test script template from deploy.sh (everything written to scripts/test.sh) +# The template spans from the first heredoc writing to test.sh until the chmod line. +_TEST_TEMPLATE_MATCH = re.search( + r"cat > scripts/test\.sh.*?chmod \+x scripts/test\.sh", + DEPLOY_CONTENT, + re.DOTALL, +) +assert _TEST_TEMPLATE_MATCH is not None, "Could not find test script template in deploy.sh" +TEST_TEMPLATE = _TEST_TEMPLATE_MATCH.group(0) + + +# ── Requirement 10.1: Executable permissions ── + +class TestExecutablePermissions: + def test_chmod_plus_x(self): + """Generated test script gets executable permissions.""" + assert "chmod +x scripts/test.sh" in DEPLOY_CONTENT + + +# ── Requirement 10.2: Optional prompt with default ── + +class TestDefaultPrompt: + def test_accepts_cli_argument_with_default(self): + """Test script accepts $1 with a default prompt value.""" + # Pattern: PROMPT="${1:-...}" or similar + assert re.search( + r'PROMPT="\$\{1:-[^}]+\}"', TEST_TEMPLATE + ), "Test script must accept optional CLI prompt with a default" + + def test_default_prompt_is_meaningful(self): + """Default prompt is a meaningful natural language query.""" + match = re.search(r'PROMPT="\$\{1:-([^}]+)\}"', TEST_TEMPLATE) + assert match is not None + default = match.group(1) + assert len(default) > 10, f"Default prompt too short: '{default}'" + + +# ── Requirement 10.3: Auth before invoke ── + +class TestAuthBeforeInvoke: + def test_cognito_auth_before_lambda_invoke(self): + """Test script authenticates with Cognito before invoking Lambda.""" + auth_pos = TEST_TEMPLATE.find("initiate-auth") + invoke_pos = TEST_TEMPLATE.find("lambda invoke") + assert auth_pos != -1, "Test script must call initiate-auth" + assert invoke_pos != -1, "Test script must call lambda invoke" + assert auth_pos < invoke_pos, ( + "initiate-auth must come before lambda invoke" + ) + + def test_uses_user_password_auth_flow(self): + """Test script uses USER_PASSWORD_AUTH flow.""" + assert "USER_PASSWORD_AUTH" in TEST_TEMPLATE + + +# ── Requirement 10.4: Lambda invocation with JWT ── + +class TestLambdaInvocation: + def test_passes_authorization_header(self): + """Test script passes Authorization header with Bearer token.""" + assert "Authorization" in TEST_TEMPLATE + assert "Bearer" in TEST_TEMPLATE + + def test_passes_prompt_in_body(self): + """Test script passes the prompt in the request body.""" + assert "prompt" in TEST_TEMPLATE + + +# ── Requirement 10.5: No nested JSON echo that breaks variable substitution ── + +class TestNoNestedJsonEcho: + def test_no_nested_echo_json_with_dollar_id_token(self): + """Test script must not use nested echo JSON that breaks ${ID_TOKEN} substitution. + + Bad pattern: echo '{"headers": {"Authorization": "Bearer '${ID_TOKEN}'"}}' + This breaks because of nested quoting. The script should use a temp file + or a heredoc approach instead. + """ + # Look for echo commands that try to inline JSON with variable substitution + bad_pattern = re.search( + r"""echo\s+['"]\s*\{.*Authorization.*\$\{?ID_TOKEN""", + TEST_TEMPLATE, + ) + assert bad_pattern is None, ( + "Test script must not use echo with inline JSON containing ${ID_TOKEN}" + ) + + def test_uses_temp_file_or_heredoc_for_payload(self): + """Test script should use a temp file or heredoc for the JSON payload.""" + uses_temp = "mktemp" in TEST_TEMPLATE or "PAYLOAD" in TEST_TEMPLATE + uses_heredoc = "<<" in TEST_TEMPLATE and "EOF" in TEST_TEMPLATE + assert uses_temp or uses_heredoc, ( + "Test script should use temp file or heredoc for JSON payload construction" + ) + + +# ── Requirement 10.1: Baked-in values ── + +class TestBakedInValues: + def test_gateway_id_baked_in(self): + """Deploy script bakes GATEWAY_ID into test script.""" + assert "GATEWAY_ID" in TEST_TEMPLATE + + def test_cognito_pool_id_baked_in(self): + """Deploy script bakes COGNITO_USER_POOL_ID into test script.""" + assert "COGNITO_USER_POOL_ID" in TEST_TEMPLATE + + def test_cognito_client_id_baked_in(self): + """Deploy script bakes COGNITO_CLIENT_ID into test script.""" + assert "COGNITO_CLIENT_ID" in TEST_TEMPLATE + + def test_lambda_function_name_baked_in(self): + """Deploy script bakes LAMBDA_FUNCTION_NAME into test script.""" + assert "LAMBDA_FUNCTION_NAME" in TEST_TEMPLATE + + def test_username_baked_in(self): + """Deploy script bakes TEST_USERNAME into test script.""" + assert "TEST_USERNAME" in TEST_TEMPLATE + + def test_password_baked_in(self): + """Deploy script bakes TEST_PASSWORD into test script.""" + assert "TEST_PASSWORD" in TEST_TEMPLATE