# Amazon Bedrock

The Bedrock adapter connects TanStack AI to [Amazon Bedrock](https://aws.amazon.com/bedrock/) with three API paths:

- **Converse** (default) — Bedrock's model-agnostic API built on `@aws-sdk/client-bedrock-runtime`. Reaches the broad chat catalog including Anthropic Claude, Amazon Nova, Meta Llama, Mistral, DeepSeek, Cohere, AI21, and OpenAI gpt-oss models.
- **Chat Completions** (`api: 'chat'`) — Bedrock's OpenAI-compatible Chat Completions endpoint. Reaches open-weight models only (gpt-oss, DeepSeek V3.x, Gemma, Qwen, Mistral open models, GLM, etc.). Does NOT reach Claude, Nova, or Llama.
- **Responses** (`api: 'responses'`) — Bedrock's OpenAI-compatible Responses API, mantle-only. Currently gpt-oss and Gemma 4.

All paths support streaming and client-side tool calling. Reasoning output is
surfaced when the model emits it (e.g. DeepSeek R1, gpt-oss); on the Converse
path, request-side enablement of extended thinking (e.g. a Claude thinking
budget) is not yet wired, so only models that reason by default surface it.

## Installation

```sh
npm i @tanstack/ai-bedrock
```

No additional packages are required. SigV4 authentication is handled by `@aws-sdk/client-bedrock-runtime`, which is a direct dependency.

## Quick Start (Converse — default)

The default `bedrockText` call uses the Converse API and reaches the broad model catalog:

```typescript ignore
// ignore: iterating a chat() stream and reading chunk.type/chunk.delta needs the
// AG-UI base event fields, which come from @ag-ui/core. It's a transitive dep of
// @tanstack/ai, so kiira (resolving @tanstack/ai from source under the dist->src
// heuristic) can't follow it and those base fields drop off StreamChunk. The code
// is correct (the same pattern is used throughout ai-client); see
// getting-started/quick-start-server for the type-checked consumption shape.
import { bedrockText } from '@tanstack/ai-bedrock'
import { chat } from '@tanstack/ai'

const adapter = bedrockText('us.anthropic.claude-haiku-4-5-20251001-v1:0', {
  region: 'us-east-1',
})

for await (const chunk of chat({
  adapter,
  messages: [{ role: 'user', content: 'What is the capital of France?' }],
})) {
  if (chunk.type === 'TEXT_MESSAGE_CONTENT') process.stdout.write(chunk.delta ?? '')
}
```

Equivalent to passing `{ api: 'converse' }` explicitly. Returns a `bedrock-converse` adapter.

## Authentication

Bedrock supports two authentication modes.

### API Key

Bedrock issues API keys from the AWS Console. See the [Bedrock API keys guide](https://docs.aws.amazon.com/bedrock/latest/userguide/api-keys.html) for instructions.

Set one of the following environment variables and the adapter picks it up automatically:

```bash
BEDROCK_API_KEY=your-bedrock-api-key
# or the legacy name:
AWS_BEARER_TOKEN_BEDROCK=your-bedrock-api-key
```

### SigV4 (AWS credential chain)

For workloads using IAM roles, instance profiles, or `~/.aws/credentials`, set `auth: 'sigv4'` (or leave it as `'auto'` with no API key in the environment). SigV4 works out of the box via `@aws-sdk/client-bedrock-runtime` — no additional packages required.

```bash
AWS_ACCESS_KEY_ID=...
AWS_SECRET_ACCESS_KEY=...
AWS_SESSION_TOKEN=...   # optional, for temporary credentials
```

### Auth resolution order (`auth: 'auto'`, the default)

1. Explicit `apiKey` passed to the factory
2. `BEDROCK_API_KEY` environment variable
3. `AWS_BEARER_TOKEN_BEDROCK` environment variable
4. SigV4 via the standard AWS credential chain

## Configuration

`BedrockClientConfig` accepts the following options:

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `api` | `'converse' \| 'chat' \| 'responses'` | `'converse'` | Bedrock API to use |
| `region` | `string` | `'us-east-1'` | AWS region string (e.g. `'us-west-2'`) |
| `auth` | `'apikey' \| 'sigv4' \| 'auto'` | `'auto'` | Authentication mode |
| `apiKey` | `string` | — | Explicit API key (overrides env vars) |
| `baseURL` | `string` | — | Override the computed base URL entirely |
| `defaultHeaders` | `Record<string, string>` | none | Headers sent with every request |
| `endpoint` | `'runtime' \| 'mantle'` | `'runtime'` | Bedrock endpoint to target (Chat Completions path only) |

The `endpoint` option only applies when `api: 'chat'`. The `runtime` endpoint (`bedrock-runtime`) hosts the broad open-weight catalog; `mantle` is an alternative. On mantle, the adapter reads the path from the Bedrock API compatibility config (`/openai/v1` for Gemma 4, `/v1` for other models). An explicit `baseURL` still wins. The Responses API always targets mantle.

## Behind a proxy

Route every request through a gateway, such as Cloudflare AI Gateway or a corporate proxy, with `baseURL` and `defaultHeaders`. These two option names are the same on every TanStack AI adapter, so one gateway config works for all of them.

```typescript
import { createBedrockText } from "@tanstack/ai-bedrock";

const adapter = createBedrockText("us.amazon.nova-pro-v1:0", process.env.BEDROCK_API_KEY!, {
  baseURL: "https://gateway.example.com/aws-bedrock",
  defaultHeaders: { "cf-aig-authorization": `Bearer ${process.env.GATEWAY_TOKEN}` },
});
```

All three Bedrock APIs accept these options. On the Converse API the headers are added before SigV4 signing, so a signed request still includes them.

## Converse API (default)

`bedrockText(model)` or `bedrockText(model, { api: 'converse' })` returns a `bedrock-converse` adapter backed by `@aws-sdk/client-bedrock-runtime`. This is Bedrock's model-agnostic conversational API and is the recommended path for most use cases.

**Model scope:** Anthropic Claude, Amazon Nova, Meta Llama, Mistral, DeepSeek, Cohere, AI21, OpenAI gpt-oss, and other models accessible in your account. See [Model availability](#model-availability) below.

```typescript
import { bedrockText } from '@tanstack/ai-bedrock'
import { chat } from '@tanstack/ai'

// Claude via Converse
const claudeAdapter = bedrockText('us.anthropic.claude-haiku-4-5-20251001-v1:0', {
  region: 'us-east-1',
})

// Amazon Nova via Converse
const novaAdapter = bedrockText('us.amazon.nova-pro-v1:0', {
  region: 'us-east-1',
})

// Meta Llama via Converse
const llamaAdapter = bedrockText('us.meta.llama4-maverick-17b-instruct-v1:0', {
  region: 'us-east-1',
})
```

### Explicit API key (Converse)

```typescript
import { createBedrockText } from '@tanstack/ai-bedrock'

const adapter = createBedrockText(
  'us.anthropic.claude-haiku-4-5-20251001-v1:0',
  'your-bedrock-api-key',
  { region: 'us-west-2' },
)
```

### Prompt caching

Add a `cachePoint` to make a prompt prefix eligible for caching. Later requests can read matching tokens at the reduced cache rate. Bedrock bills cache misses at the standard input rate.

Explicit prompt caching is model-dependent. Use `cachePoint` only with a model that AWS lists as supporting it. The minimum checkpoint size and the TTL options also vary by model.

Set `metadata.cachePoint` on the item that ends the stable part of your prompt. The adapter places a `cachePoint` block right after it:

```typescript
import { bedrockText } from '@tanstack/ai-bedrock'
import { chat } from '@tanstack/ai'

const stream = chat({
  adapter: bedrockText('us.anthropic.claude-sonnet-4-5-20250929-v1:0', {
    region: 'us-east-1',
  }),
  systemPrompts: [
    {
      content: 'Long, stable instructions...',
      metadata: { cachePoint: { type: 'default' } },
    },
  ],
  messages: [
    // An older turn. Its cachePoint was removed when the new turn was added.
    { role: 'user', content: 'Summarize the report.' },
    { role: 'assistant', content: 'The report says...' },
    {
      role: 'user',
      content: [
        {
          type: 'text',
          content: 'What changed since yesterday?',
          // Only the newest message has a checkpoint.
          // It caches the conversation up to here for the next round.
          metadata: { cachePoint: { type: 'default' } },
        },
      ],
    },
  ],
})
```

Keep one message checkpoint, on the newest message. Before you send the next turn, remove `cachePoint` from the older message and add it to the new message. The metadata stays in the message history. If you do not remove it, the checkpoints add up, and the request can go over the Bedrock limit of four.

Tools take the same metadata. Pass `metadata: { cachePoint: { type: 'default' } }` to `toolDefinition()` for the last tool in the list to cache the tool definitions.

- Bedrock accepts up to four checkpoints per request.
- Add `ttl: '1h'` to keep an entry for one hour. The default is 5 minutes, and a read refreshes the timer. Bedrock processes tool checkpoints first, then system prompt checkpoints, and then message checkpoints. If a request mixes both TTLs, place every `1h` checkpoint before the first `5m` checkpoint in that combined order.
- A checkpoint below the model's minimum size is ignored. The request still succeeds, and nothing is cached.

### Token usage

`onUsage` and `RUN_FINISHED.usage` report Bedrock's counts as `promptTokens`, `completionTokens`, and `totalTokens`. When a request hits or writes a prompt cache, the cache counts arrive on `promptTokensDetails.cachedTokens` and `promptTokensDetails.cacheWriteTokens`. Bedrock counts only the uncached part of the input in `promptTokens`, so add the two cache counts to it to get the full input size.

## Chat Completions API (`api: 'chat'`)

Set `api: 'chat'` to use Bedrock's OpenAI-compatible Chat Completions endpoint. Returns a `bedrock` adapter.

**Model scope:** Open-weight models only — gpt-oss, DeepSeek V3.x, Gemma, Qwen, Mistral open models, GLM, and similar. Claude, Nova, and Llama are NOT available on this endpoint. See the [AWS API compatibility matrix](https://docs.aws.amazon.com/bedrock/latest/userguide/models-api-compatibility.html) for the current list.

```typescript ignore
// ignore: see the Converse quick-start above — iterating a chat() stream and
// reading chunk.type/chunk.delta needs @ag-ui/core base fields kiira can't
// resolve transitively through @tanstack/ai source.
import { bedrockText } from '@tanstack/ai-bedrock'
import { chat } from '@tanstack/ai'

const adapter = bedrockText('openai.gpt-oss-20b-1:0', {
  region: 'us-east-1',
  api: 'chat',
})

for await (const chunk of chat({
  adapter,
  messages: [{ role: 'user', content: 'What is the capital of France?' }],
})) {
  if (chunk.type === 'TEXT_MESSAGE_CONTENT') process.stdout.write(chunk.delta ?? '')
}
```

## Responses API (`api: 'responses'`)

Set `api: 'responses'` to use Bedrock's OpenAI-compatible Responses API. Returns a `bedrock-responses` adapter. This API is mantle-only.

**Model scope:** Currently gpt-oss and Gemma 4. The Responses API is stateful — pass `previous_response_id` and `store` through `modelOptions` to continue a conversation server-side.

**Reasoning:** Reasoning deltas stream as thinking content. Mantle-served models may still emit the pre-July-2025 event name `response.reasoning.delta` (removed from the OpenAI spec in favor of `response.reasoning_text.delta`); the adapter recognizes both and maps them identically.

```typescript ignore
// ignore: see the Converse quick-start above — iterating a chat() stream and
// reading chunk.type/chunk.delta needs @ag-ui/core base fields kiira can't
// resolve transitively through @tanstack/ai source.
import { bedrockText } from '@tanstack/ai-bedrock'
import { chat } from '@tanstack/ai'

const adapter = bedrockText('openai.gpt-oss-120b-1:0', {
  region: 'us-east-1',
  api: 'responses',
})

for await (const chunk of chat({
  adapter,
  messages: [{ role: 'user', content: 'Summarize the Bedrock pricing page.' }],
})) {
  if (chunk.type === 'TEXT_MESSAGE_CONTENT') process.stdout.write(chunk.delta ?? '')
}
```

## Embeddings

Generate embedding vectors with Titan or Cohere embedding models via `InvokeModel`:

```typescript
import { embed } from "@tanstack/ai";
import { bedrockEmbedding } from "@tanstack/ai-bedrock";

const result = await embed({
  adapter: bedrockEmbedding("amazon.titan-embed-text-v2:0"),
  input: ["a red guitar", "a blue drum kit"],
  dimensions: 512, // 256 | 512 | 1024
});

console.log(result.embeddings[0]?.vector);
```

Titan Multimodal embeds text and images — alone or fused into a single vector. Fuse parts by nesting them in an array (the same `Array<ContentPart>` shape a chat message's `content` uses); the outer array is the item list, so this embeds one fused item:

```typescript
import { embed } from "@tanstack/ai";
import { bedrockEmbedding } from "@tanstack/ai-bedrock";

const productPhoto = "iVBORw0KGgo..."; // base64 image data

const result = await embed({
  adapter: bedrockEmbedding("amazon.titan-embed-image-v1"),
  input: [
    [
      { type: "text", content: "a red guitar" },
      {
        type: "image",
        source: {
          type: "data",
          value: productPhoto,
          mimeType: "image/png",
        },
      },
    ],
  ],
  dimensions: 1024, // 256 | 384 | 1024
});
```

Cohere Embed v3 on Bedrock is also supported (text-only, batched, requires `inputType`):

```typescript
import { embed } from "@tanstack/ai";
import { bedrockEmbedding } from "@tanstack/ai-bedrock";

const result = await embed({
  adapter: bedrockEmbedding("cohere.embed-english-v3"),
  input: ["a red guitar", "a blue drum kit"],
  modelOptions: { inputType: "search_document" },
});
```

> Titan models have no batch API — a batch of N items runs as N `InvokeModel`
> calls under a small concurrency cap. Titan Multimodal does not fetch remote
> image URLs; pass base64 data (or a `data:` URI).

See the [Embeddings guide](../embeddings.md) for the full API.

## Model Availability

The adapter ships with a hand-seeded snapshot catalog (`src/model-catalog.generated.ts`) of confirmed model IDs. This catalog can be refreshed by the maintainer script `scripts/fetch-bedrock-models.ts`, which calls `ListFoundationModels` with AWS credentials.

**Actual model availability depends on your AWS account's model access configuration and the region you are targeting.** Enable model access in the [Amazon Bedrock console](https://console.aws.amazon.com/bedrock/home#/modelaccess) before use.

For the full list of models and which API endpoints they support, see the [AWS API compatibility matrix](https://docs.aws.amazon.com/bedrock/latest/userguide/models-api-compatibility.html).

## Supported Capabilities

- Streaming chat completions
- Client-side tool calling
- Reasoning output (extended thinking surfaced when the model emits it; request-side enablement on Converse is not yet wired)
- Multimodal input (text, images, documents — model-dependent)
- JSON schema / structured output

## API Reference

### `bedrockText(model, config?)`

Creates a Bedrock adapter using environment-variable auth.

- `model` — Model ID (e.g. `'us.anthropic.claude-haiku-4-5-20251001-v1:0'`)
- `config.api` — `'converse'` (default), `'chat'`, or `'responses'`
- `config.region` — AWS region string (default `'us-east-1'`)
- `config.auth` — `'auto'` (default), `'apikey'`, or `'sigv4'`
- `config.apiKey` — Explicit API key (overrides env vars)
- `config.baseURL` — Override base URL
- `config.endpoint` — `'runtime'` (default) or `'mantle'` (Chat Completions path only)

Returns a chat adapter for use with `chat()` or `generate()`.

| `api` value | Adapter name | Underlying SDK |
|---|---|---|
| `'converse'` (default) | `bedrock-converse` | `@aws-sdk/client-bedrock-runtime` |
| `'chat'` | `bedrock` | `openai` (OpenAI-compatible) |
| `'responses'` | `bedrock-responses` | `openai` (OpenAI-compatible) |

### `createBedrockText(model, apiKey, config?)`

Creates a Bedrock adapter with an explicit API key, bypassing the environment-variable lookup.

## Next Steps

- [Amazon Bedrock API keys](https://docs.aws.amazon.com/bedrock/latest/userguide/api-keys.html) — Create and manage API keys
- [Amazon Bedrock model access](https://docs.aws.amazon.com/bedrock/latest/userguide/model-access.html) — Enable models in your account
- [AWS API compatibility matrix](https://docs.aws.amazon.com/bedrock/latest/userguide/models-api-compatibility.html) — Which models work with which APIs
- [Converse API reference](https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_Converse.html) — Native Converse API docs
- [Streaming Guide](../chat/streaming) — Learn about streaming responses
- [Tools Guide](../tools/tools) — Learn about tool calling
