# Warlock AI Bedrock — full skills

> Package: `@warlock.js/ai-bedrock`

> Generated artifact. Concatenates every SKILL.md and reference file under `@warlock.js/ai-bedrock/skills/`. Re-run `node scripts/generate-llms.mjs` after any change.

## setup-bedrock  `@warlock.js/ai-bedrock/setup-bedrock/SKILL.md`

---
name: setup-bedrock
description: 'Wire @warlock.js/ai-bedrock — new BedrockSDK({region, credentials?, provider?}) for AWS Bedrock Converse API + Titan embeddings. AWS credential chain (no apiKey). Triggers: `BedrockSDK`, `bedrock.model`, `bedrock.embedder`, `bedrock.count`, `BedrockRuntimeClient`; "how do I use AWS Bedrock", "wire Claude/Nova/Llama on Bedrock", "Titan embeddings", "Bedrock Converse API"; typical import `import { BedrockSDK } from "@warlock.js/ai-bedrock"`. Skip: agent wiring — `@warlock.js/ai/run-ai-agent/SKILL.md`; provider choice — `@warlock.js/ai/pick-ai-provider/SKILL.md`; embeddings concepts — `@warlock.js/ai/embed-text/SKILL.md`; competing libs `@aws-sdk/client-bedrock-runtime`, `@ai-sdk/amazon-bedrock`; sibling adapters `@warlock.js/ai-openai`, `@warlock.js/ai-anthropic`, `@warlock.js/ai-google`, `@warlock.js/ai-ollama`.'
---

# `@warlock.js/ai-bedrock`

Provider adapter that turns AWS Bedrock's **Converse API** into a vendor-neutral `ModelContract`, plus an Amazon **Titan** embedder. One wire mapping covers every Bedrock family.

## Construction

```ts
import { BedrockSDK } from "@warlock.js/ai-bedrock";

// Ambient AWS credential chain (env vars / SSO / IAM role):
const bedrock = new BedrockSDK({ region: "us-east-1" });

// Explicit static credentials:
const bedrock2 = new BedrockSDK({
  region: "us-east-1",
  credentials: { accessKeyId: "AKIA...", secretAccessKey: "..." },
  provider: "bedrock-eu",
});
```

Bedrock authenticates via the **AWS credential chain, not an API key** — there is no `apiKey`. `region` is required; `credentials` is optional (omit to use the standard provider chain). The whole config object is forwarded to `BedrockRuntimeClient`, so any AWS client option (`endpoint`, `requestHandler`, retry config) is accepted. `provider` defaults to `"bedrock"`.

## Producing a model

`name` is the Bedrock **model id or inference-profile id**:

```ts
bedrock.model({ name: "anthropic.claude-sonnet-4-5-20250929-v1:0" })
bedrock.model({ name: "us.amazon.nova-pro-v1:0", temperature: 0.2 })
bedrock.model({ name: "meta.llama3-1-8b-instruct-v1:0", maxTokens: 1024 })
```

Uses the model-agnostic **Converse / ConverseStream** API — one wire mapping covers every Bedrock family (Claude, Nova, Llama, Mistral, Cohere) instead of per-family `InvokeModel` body shapes.

## Capabilities — what's auto-set

| Flag | Default |
| --- | --- |
| `structuredOutput` | `true` (via Converse `outputConfig.textFormat`) |
| `vision` | Inferred from model id substring. `true` for Claude 3/3.5/3.7/4, Nova Lite/Pro/Premier, Llama 3.2-11B/90B, Llama 4. |

Explicit config always wins.

## System prompt

Converse has no `"system"` role in `messages`. The adapter hoists every neutral `role: "system"` message into the separate `system: SystemContentBlock[]` request field. Transparent to the agent.

## Tool calls

- Outgoing: neutral tools → `toolConfig.tools[].toolSpec` with a JSON `inputSchema`. No tools → `toolConfig` omitted entirely (Bedrock rejects an empty `tools` array).
- Assistant tool calls in history → `assistant` message with optional leading `{ text }` + one `{ toolUse }` block per call.
- Tool results (`role: "tool"`) → a `user` turn with one `{ toolResult: { toolUseId, content: [{ text }] } }` block.
- Response `toolUse` blocks → neutral `toolCalls`; `stopReason: "tool_use"` → `finishReason: "tool_calls"`.

## Structured output

Object-root `responseSchema` + `structuredOutput`-capable → `outputConfig.textFormat = { type: "json_schema", structure: { jsonSchema: { name, schema } } }` — note Bedrock wants the schema as a **stringified JSON document**.

## Multipart messages (vision)

- `{ type: "text" }` → `{ text }`
- `{ type: "image", source: { base64, mediaType } }` → `{ image: { format, source: { bytes } } }` (base64 decoded; media type → `jpeg`/`png`/`gif`/`webp`)
- `{ type: "image", source: { url } }` → **throws `InvalidRequestError`**. Bedrock's `ImageSource` only accepts raw bytes or an S3 location. Resolve images to base64 first.

## Streaming

`model.stream()` drains `ConverseStreamCommand`'s `response.stream`:

- `contentBlockDelta.delta.text` → `{ type: "delta", content }`
- `toolUse`: `contentBlockStart` opens an accumulator, `contentBlockDelta.delta.toolUse.input` fragments append, `contentBlockStop` emits one consolidated `{ type: "tool-call", ... }`
- terminal `{ type: "done", finishReason, usage }` — usage from `metadata.usage` (a `TokenUsage`)

## Finish-reason mapping

`end_turn` / `stop_sequence` → `stop` · `max_tokens` → `length` · `tool_use` → `tool_calls` · `content_filtered` / `guardrail_intervened` / unknown / null → `error`.

## Embeddings — Amazon Titan only

```ts
const embedder = bedrock.embedder({ name: "amazon.titan-embed-text-v2:0" });
const { vector } = await embedder.embed("Hello world");
const { vectors } = await embedder.embedMany(["a", "b"]);   // N sequential calls
```

Targets the **Titan Text Embeddings** family via `InvokeModel`. **Titan has no batch endpoint** — `embedMany` issues one request per input sequentially and aggregates token usage. Cohere-on-Bedrock batch embeddings use an incompatible body shape and are out of scope — use the OpenAI adapter when batch throughput matters.

Pass `dimensions` to forward Titan v2's truncation hint (256 / 512 / 1024).

## Errors

Raw AWS SDK exceptions are wrapped into the typed `@warlock.js/ai` `AIError` hierarchy. Dispatch keys on the Smithy exception `name`:

- `AccessDeniedException` / 401 / 403 → `ProviderAuthError`
- `ThrottlingException` / 429 → `ProviderRateLimitError`
- `ServiceQuotaExceededException` → `QuotaExceededError`
- `ValidationException` with "too long / context window" → `ContextLengthExceededError`, else `InvalidRequestError`
- `ModelTimeoutException` / `ETIMEDOUT` → `ProviderTimeoutError`
- 5xx / `ModelError` / `ServiceUnavailable` → `ProviderError`

## Token counting

```ts
await bedrock.count("some text")  // approximate heuristic, offline
```

Per-model tokenizers differ across Bedrock families.

## Pricing — per-model registry

`pricing` is an optional **registry keyed by Bedrock model id** — one entry per model, all rates in **USD per 1,000,000 tokens** (`ModelPricing`: `input`, `output`, optional `cachedInput` / `cachedOutput`). Set it on the SDK so every model it produces inherits the rates:

```ts
const bedrock = new BedrockSDK({
  region: "us-east-1",
  pricing: {
    // USD per 1M tokens. Key = exact Bedrock model id / inference-profile id.
    "anthropic.claude-sonnet-4-5-20250929-v1:0": { input: 3, output: 15 },
    "us.amazon.nova-pro-v1:0":                    { input: 0.8, output: 3.2 },
  },
});

const { usage } = await ai.agent({ model: bedrock.model({ name: "us.amazon.nova-pro-v1:0" }) }).execute("hi");
usage.cost;  // per-channel USD breakdown of THIS run, computed from tokens × pricing[model]
```

Resolution at `model()` time: per-model `pricing` (`bedrock.model({ name, pricing })`) > SDK registry > `undefined` (no cost computed). When the same model id appears under a cross-region inference profile, list each id you actually invoke (`us.`, `eu.`, `apac.` prefixes are distinct keys). Bedrock meters cache-read tokens via Converse `usage` — those surface as `usage.cachedTokens`, so set `cachedInput` to have them priced at the discounted rate. See [`@warlock.js/ai/pick-ai-provider/SKILL.md`](@warlock.js/ai/pick-ai-provider/SKILL.md).

## When NOT to use this skill

- Direct `@aws-sdk/client-bedrock-runtime` calls without going through `@warlock.js/ai` agents.
- OpenAI / Anthropic-direct / Gemini / Ollama models — those have their own adapter packages.
- Batch embeddings throughput — Titan is single-input upstream; use the OpenAI / Google / Ollama adapter (those batch natively).

## See also

- [`@warlock.js/ai/run-ai-agent/SKILL.md`](@warlock.js/ai/run-ai-agent/SKILL.md)
- [`@warlock.js/ai/pick-ai-provider/SKILL.md`](@warlock.js/ai/pick-ai-provider/SKILL.md)
- [`@warlock.js/ai/embed-text/SKILL.md`](@warlock.js/ai/embed-text/SKILL.md)


