Tools
Bedrock Cache
OpenClaw plugin that injects Bedrock Converse API cachePoint blocks for prompt caching on Claude models (~90% input cost savings)
Configuration Example
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"bedrock:InvokeModel",
"bedrock:InvokeModelWithResponseStream"
],
"Resource": [
"arn:aws:bedrock:*::foundation-model/anthropic.*",
"arn:aws:bedrock:us-east-1:ACCOUNT_ID:inference-profile/us.anthropic.*"
]
},
{
"Effect": "Allow",
"Action": "bedrock:ListFoundationModels",
"Resource": "*"
}
]
}
README
# openclaw-bedrock-cache-plugin
OpenClaw plugin that injects [Bedrock Converse API `cachePoint` blocks](https://docs.aws.amazon.com/bedrock/latest/userguide/prompt-caching.html) into API payloads, enabling prompt caching for Claude models on Amazon Bedrock.
**Result: ~90% input cost savings** on cached prefixes (system prompts, tool definitions).
## How it works
The Bedrock Converse API supports prompt caching via `cachePoint` marker blocks placed after static content. Cached prefixes are reused across requests within a 5-minute TTL window, charged at 10% of normal input token cost.
This plugin is a forked replacement for OpenClaw's built-in `amazon-bedrock` provider. It wraps the stream function via `streamWithPayloadPatch` to inject `{ cachePoint: { type: "default" } }` after:
1. **System prompt** — end of `system[]` array (skipped if OpenClaw core already adds one)
2. **Tool definitions** — end of `toolConfig.tools[]` array
All other built-in behavior is preserved: region resolution, error classification, replay hooks, guardrail support.
## Validated results
Tested with OpenClaw v2026.4.5 on Claude Sonnet 4.6 via Bedrock, confirmed via CloudWatch model invocation logs:
| Turn | inputTokens | cacheWrite | cacheRead | Savings |
|------|------------|------------|-----------|---------|
| 1st | 3 | 155,078 | 0 | (cache write) |
| 2nd | 3 | 34 | 155,078 | ~90% |
## Requirements
- OpenClaw v2026.4.x or later
- Amazon Bedrock access with Claude models (model access must be enabled in the AWS console)
- AWS IAM user or role with Bedrock invoke permissions
## AWS Configuration
### IAM permissions
The IAM user/role running OpenClaw needs these permissions:
```json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"bedrock:InvokeModel",
"bedrock:InvokeModelWithResponseStream"
],
"Resource": [
"arn:aws:bedrock:*::foundation-model/anthropic.*",
"arn:aws:bedrock:us-east-1:ACCOUNT_ID:inference-profile/us.anthropic.*"
]
},
{
"Effect": "Allow",
"Action": "bedrock:ListFoundationModels",
"Resource": "*"
}
]
}
```
Replace `ACCOUNT_ID` with your AWS account ID and adjust the region if needed.
### AWS credentials
Set credentials as environment variables in your OpenClaw gateway service (e.g., systemd unit file):
```ini
# In your systemd service [Service] section or .env file:
Environment=AWS_ACCESS_KEY_ID=AKIA...
Environment=AWS_SECRET_ACCESS_KEY=...
Environment=AWS_REGION=us-east-1
```
Or if using named profiles:
```ini
Environment=AWS_PROFILE=your-profile
Environment=AWS_REGION=us-east-1
```
The plugin detects credentials via the standard AWS SDK resolution order: environment variables (`AWS_ACCESS_KEY_ID` + `AWS_SECRET_ACCESS_KEY`), AWS profile (`AWS_PROFILE`), or bearer token (`AWS_BEARER_TOKEN_FILE`).
### OpenClaw configuration
Configure Bedrock as the model provider in `~/.openclaw/openclaw.json`:
```json
{
"agents": {
"defaults": {
"model": {
"primary": "amazon-bedrock/us.anthropic.claude-sonnet-4-6",
"fallbacks": [
"amazon-bedrock/us.anthropic.claude-sonnet-4-5-20250929-v1:0"
]
},
"params": {
"cacheRetention": "short"
}
}
},
"plugins": {
"entries": {
"amazon-bedrock": {
"enabled": false,
"config": {
"discovery": {
"enabled": true,
"region": "us-east-1",
"providerFilter": ["anthropic"],
"refreshInterval": 0,
"defaultContextWindow": 32000,
"defaultMaxTokens": 4096
}
}
},
"bedrock-cache": {
"enabled": true
}
}
}
}
```
Key settings:
| Field | Purpose |
|-------|---------|
| `agents.defaults.model.primary` | Bedrock model ID — use `amazon-bedrock/` prefix + inference profile ID |
| `agents.defaults.params.cacheRetention` | Set to `"short"` to enable OpenClaw's system prompt cache markers |
| `plugins.entries.amazon-bedrock.enabled` | **Must be `false`** — disables the built-in provider |
| `plugins.entries.amazon-bedrock.config.discovery` | Model discovery config (still read even when disabled, used by gateway) |
| `plugins.entries.bedrock-cache.enabled` | **Must be `true`** — enables this plugin |
#### Model IDs
Bedrock model IDs use the format `amazon-bedrock/<model-id>`. Common Claude models:
| Model | ID |
|-------|----|
| Claude Sonnet 4.6 | `amazon-bedrock/us.anthropic.claude-sonnet-4-6` |
| Claude Sonnet 4.5 | `amazon-bedrock/us.anthropic.claude-sonnet-4-5-20250929-v1:0` |
| Claude Haiku 4.5 | `amazon-bedrock/us.anthropic.claude-haiku-4-5-20251001-v1:0` |
Use `us.anthropic.*` prefixed IDs (cross-region inference profiles) for automatic region routing, or `anthropic.*` for direct model IDs locked to a single region.
## Installation
### Step 1: Copy to bundled extensions directory
The plugin **must** be installed in OpenClaw's bundled extensions directory, not the global extensions directory. This is because OpenClaw's `bundledProviderAllowlistCompat` mode only allows `wrapStreamFn` hooks from plugins with `origin: "bundled"`.
```bash
# Find your OpenClaw dist directory
OPENCLAW_DIST=$(dirname $(readlink -f $(which openclaw)))/../dist
# Or if installed globally via pnpm:
# OPENCLAW_DIST=~/.local/share/pnpm/global/5/node_modules/openclaw/dist
# Copy plugin files
mkdir -p "$OPENCLAW_DIST/extensions/bedrock-cache"
cp index.js package.json openclaw.plugin.json "$OPENCLAW_DIST/extensions/bedrock-cache/"
```
### Step 2: Disable the built-in amazon-bedrock plugin
```bash
openclaw plugins disable amazon-bedrock
```
### Step 3: Restart the gateway
```bash
# systemd
systemctl --user restart openclaw-gateway
# or manual
openclaw gateway --force
```
### Step 4: Verify
```bash
openclaw plugins doctor
```
You should see no errors for `bedrock-cache`. The diagnostic `provider already registered: amazon-bedrock` from `plugins doctor` CLI is expected (the CLI loads both plugins in its own context; the gateway only loads ours).
## After OpenClaw upgrades
OpenClaw upgrades replace the `dist/extensions/` directory, removing the plugin. Re-run Step 1 and restart the gateway after upgrading.
## Monitoring cache performance
Enable [Bedrock model invocation logging](https://docs.aws.amazon.com/bedrock/latest/userguide/model-invocation-logging.html) to see cache metrics in CloudWatch:
```bash
# Create log group
aws logs create-log-group --log-group-name /aws/bedrock/model-invocation-logs
# Create service role for Bedrock to write logs
aws iam create-role \
--role-name BedrockModelInvocationLoggingRole \
--assume-role-policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"Service": "bedrock.amazonaws.com"},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {"aws:SourceAccount": "ACCOUNT_ID"},
"ArnLike": {"aws:SourceArn": "arn:aws:bedrock:us-east-1:ACCOUNT_ID:*"}
}
}]
}'
aws iam put-role-policy \
--role-name BedrockModelInvocationLoggingRole \
--policy-name CloudWatchLogsWrite \
--policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["logs:CreateLogStream", "logs:PutLogEvents"],
"Resource": "arn:aws:logs:us-east-1:ACCOUNT_ID:log-group:/aws/bedrock/model-invocation-logs:*"
}]
}'
# Enable logging
aws bedrock put-model-invocation-logging-configuration \
--logging-config '{
"cloudWatchConfig": {
"logGroupName": "/aws/bedrock/model-invocation-logs",
"roleArn": "arn:aws:iam::ACCOUNT_ID:role/BedrockModelInvocationLoggingRole"
},
"textDataDeliveryEnabled": true
}'
```
Query cache metrics with CloudWatch Logs Insights:
```
fields @timestamp,
output.outputBodyJson.usage.inputTokens as input,
output.outputBodyJson.usage.cacheWriteInputTokens as cacheWrite,
output.outputBodyJson.usage.cacheReadInputTokens as cacheRead,
output.outputBodyJson.usage.outputTokens as output
| filter operation = "ConverseStream"
| sort @timestamp desc
| limit 50
```
## Why not a standard plugin install?
OpenClaw's plugin system has a `bundledProviderAllowlistCompat` mode that restricts `wrapStreamFn` hooks to plugins with `origin: "bundled"`. Plugins installed via `openclaw plugins install` get `origin: "global"` and their `wrapStreamFn` hooks are silently ignored for built-in provider IDs.
This means the plugin must be placed directly in `dist/extensions/` alongside the built-in plugins. This is the only way for the `wrapStreamFn` hook to fire when Bedrock models are invoked.
## Cost impact
For a 155k token system prompt + tool definitions (typical for OpenClaw agents):
| | Without caching | With caching (after 1st turn) |
|---|---|---|
| Input cost per turn | ~$0.47 | ~$0.047 |
| Savings | — | **~$0.42/turn** |
Cache write (first turn) costs ~25% more than normal input. Break-even after ~1.3 turns.
## License
MIT
tools
Comments
Sign in to leave a comment