|
| 1 | +/** |
| 2 | + * MIT No Attribution |
| 3 | + * |
| 4 | + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. |
| 5 | + * |
| 6 | + * Permission is hereby granted, free of charge, to any person obtaining a copy of |
| 7 | + * the Software without restriction, including without limitation the rights to |
| 8 | + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of |
| 9 | + * the Software, and to permit persons to whom the Software is furnished to do so. |
| 10 | + * |
| 11 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 12 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 13 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| 14 | + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 15 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 16 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| 17 | + * SOFTWARE. |
| 18 | + */ |
| 19 | + |
| 20 | +import { Node } from 'constructs'; |
| 21 | + |
| 22 | +/** |
| 23 | + * Single source of truth for the Bedrock **foundation-model IDs** the agent |
| 24 | + * runtime may invoke. Both grant sites — the AgentCore runtime in |
| 25 | + * `stacks/agent.ts` and the ECS task role in `constructs/ecs-agent-cluster.ts` |
| 26 | + * — derive their `grantInvoke` / IAM ARNs from this one list, so the two |
| 27 | + * backends can never drift (they were previously two hand-synced arrays; #433). |
| 28 | + * |
| 29 | + * Scoping is intentionally per-model (explicit foundation-model + |
| 30 | + * cross-Region inference-profile ARNs), NOT a `Resource: '*'` wildcard — that |
| 31 | + * hardening is preserved. Account-level Bedrock model access remains the outer |
| 32 | + * gate; this list only controls the IAM grant. |
| 33 | + */ |
| 34 | +export const DEFAULT_BEDROCK_MODEL_IDS: readonly string[] = [ |
| 35 | + 'anthropic.claude-sonnet-4-6', |
| 36 | + 'anthropic.claude-opus-4-20250514-v1:0', |
| 37 | + 'anthropic.claude-haiku-4-5-20251001-v1:0', |
| 38 | +]; |
| 39 | + |
| 40 | +/** CDK context key whose value (a string array) overrides the model set. */ |
| 41 | +export const BEDROCK_MODELS_CONTEXT_KEY = 'bedrockModels'; |
| 42 | + |
| 43 | +/** |
| 44 | + * Resolves the invocable foundation-model IDs: CDK context `bedrockModels` |
| 45 | + * (an array of **bare foundation-model IDs**) when provided, else |
| 46 | + * {@link DEFAULT_BEDROCK_MODEL_IDS}. Set via `cdk.json` `context` or |
| 47 | + * `-c bedrockModels='["anthropic.claude-opus-4-8", …]'`, then redeploy, to add |
| 48 | + * a model the runtime may invoke — no construct edits needed. |
| 49 | + * |
| 50 | + * **Use the bare foundation-model ID (`anthropic.claude-…`), NOT the |
| 51 | + * `us.`-prefixed inference-profile ID.** Both grant sites derive the US |
| 52 | + * inference-profile ARN by prefixing `us.`, so passing `us.anthropic.…` here |
| 53 | + * would produce an invalid `us.us.anthropic.…` ARN. The resolver rejects a |
| 54 | + * `us.`/`eu.`/`apac.`-prefixed entry to catch that early. |
| 55 | + * |
| 56 | + * Throws on a malformed override (non-array, non-string / empty entries, or a |
| 57 | + * region-prefixed ID) so a typo fails synth loudly instead of silently |
| 58 | + * granting nothing or an invalid ARN. |
| 59 | + */ |
| 60 | +export function resolveBedrockModelIds(node: Node): readonly string[] { |
| 61 | + const override = node.tryGetContext(BEDROCK_MODELS_CONTEXT_KEY); |
| 62 | + if (override === undefined || override === null) { |
| 63 | + return DEFAULT_BEDROCK_MODEL_IDS; |
| 64 | + } |
| 65 | + if (!Array.isArray(override) || override.length === 0) { |
| 66 | + throw new Error( |
| 67 | + `Context '${BEDROCK_MODELS_CONTEXT_KEY}' must be a non-empty array of foundation-model IDs ` |
| 68 | + + `(e.g. ["anthropic.claude-sonnet-4-6"]); got ${JSON.stringify(override)}.`, |
| 69 | + ); |
| 70 | + } |
| 71 | + for (const id of override) { |
| 72 | + if (typeof id !== 'string' || id.trim().length === 0) { |
| 73 | + throw new Error( |
| 74 | + `Context '${BEDROCK_MODELS_CONTEXT_KEY}' entries must be non-empty strings; got ${JSON.stringify(id)}.`, |
| 75 | + ); |
| 76 | + } |
| 77 | + if (/^(us|eu|apac)\./.test(id)) { |
| 78 | + throw new Error( |
| 79 | + `Context '${BEDROCK_MODELS_CONTEXT_KEY}' expects bare foundation-model IDs, not region-prefixed ` |
| 80 | + + `inference-profile IDs — got '${id}'. Use '${id.replace(/^(us|eu|apac)\./, '')}'; ` |
| 81 | + + 'the US inference-profile ARN is derived automatically.', |
| 82 | + ); |
| 83 | + } |
| 84 | + } |
| 85 | + return override as string[]; |
| 86 | +} |
0 commit comments