opencode-crs-bedrock 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +206 -0
- package/bun.lock +19 -0
- package/dist/index.js +547 -0
- package/index.ts +1060 -0
- package/package.json +19 -0
package/README.md
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# opencode-crs-bedrock
|
|
2
|
+
|
|
3
|
+
OpenCode plugin for FeedMob CRS (Claude Relay Service) proxy to AWS Bedrock Anthropic models.
|
|
4
|
+
|
|
5
|
+
## Why This Plugin?
|
|
6
|
+
|
|
7
|
+
When using CRS to proxy requests to AWS Bedrock's Anthropic models, the response format differs from the native Anthropic API in ways that break `@ai-sdk/anthropic`:
|
|
8
|
+
|
|
9
|
+
| Issue | What CRS/Bedrock Returns | What SDK Expects |
|
|
10
|
+
|-------|--------------------------|------------------|
|
|
11
|
+
| Model IDs | `us.anthropic.claude-sonnet-4-20250514-v1:0` | `claude-sonnet-4-20250514` |
|
|
12
|
+
| Message start | `{type: "message", ...}` | `{type: "message_start", message: {...}}` |
|
|
13
|
+
| Tool calls | Missing `content_block_start` events | `content_block_start` with tool name and ID |
|
|
14
|
+
| Block close | Missing `content_block_stop` events | `content_block_stop` before `message_delta` |
|
|
15
|
+
|
|
16
|
+
This plugin transforms the CRS response stream to match what the Anthropic SDK expects.
|
|
17
|
+
|
|
18
|
+
## Features
|
|
19
|
+
|
|
20
|
+
- **SSE Stream Transformation** - Fixes discriminator validation errors
|
|
21
|
+
- **Tool Name Inference** - Buffers tool call deltas and infers correct tool name from parameters
|
|
22
|
+
- **Event Injection** - Adds missing `content_block_start` and `content_block_stop` events
|
|
23
|
+
- **Model ID Mapping** - Converts Bedrock model IDs to standard Anthropic format
|
|
24
|
+
- **API Key Auth** - Uses `x-api-key` header (bypasses SDK's `sk-ant-` validation)
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
### Local Development
|
|
29
|
+
|
|
30
|
+
Reference directly in your OpenCode config:
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"plugin": [
|
|
35
|
+
"file:///path/to/opencode-crs-bedrock"
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### From npm (when published)
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npm install -g opencode-crs-bedrock
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Configuration
|
|
47
|
+
|
|
48
|
+
### 1. Set Environment Variables
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
export ANTHROPIC_BASE_URL=https://your-crs-endpoint.com/api
|
|
52
|
+
export ANTHROPIC_AUTH_TOKEN=cr_your_api_key_here
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Add to your shell profile (`~/.bashrc`, `~/.zshrc`) to persist.
|
|
56
|
+
|
|
57
|
+
### 2. Create OpenCode Config
|
|
58
|
+
|
|
59
|
+
Create `opencode.json` in your project or `~/.config/opencode/opencode.json` globally:
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"$schema": "https://opencode.ai/config.json",
|
|
64
|
+
"plugin": [
|
|
65
|
+
"opencode-crs-bedrock"
|
|
66
|
+
],
|
|
67
|
+
"provider": {
|
|
68
|
+
"crs": {
|
|
69
|
+
"npm": "@ai-sdk/anthropic",
|
|
70
|
+
"options": {
|
|
71
|
+
"baseURL": "{env:ANTHROPIC_BASE_URL}",
|
|
72
|
+
"apiKey": "{env:ANTHROPIC_AUTH_TOKEN}"
|
|
73
|
+
},
|
|
74
|
+
"models": {
|
|
75
|
+
"claude-opus-4-5": {
|
|
76
|
+
"name": "Opus 4.5 [CRS]",
|
|
77
|
+
"limit": { "context": 200000, "output": 64000 },
|
|
78
|
+
"modalities": { "input": ["text", "image", "pdf"], "output": ["text"] },
|
|
79
|
+
"variants": {
|
|
80
|
+
"low": { "thinkingConfig": { "thinkingBudget": 8192 } },
|
|
81
|
+
"max": { "thinkingConfig": { "thinkingBudget": 32768 } }
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
"claude-sonnet-4-5": {
|
|
85
|
+
"name": "Sonnet 4.5 [CRS]",
|
|
86
|
+
"modalities": { "input": ["text", "image", "pdf"], "output": ["text"] },
|
|
87
|
+
"variants": {
|
|
88
|
+
"low": { "thinkingConfig": { "thinkingBudget": 8192 } },
|
|
89
|
+
"max": { "thinkingConfig": { "thinkingBudget": 32768 } }
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### 3. Use the Model
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
# Interactive mode
|
|
102
|
+
opencode
|
|
103
|
+
|
|
104
|
+
# Or direct run with default model
|
|
105
|
+
opencode run -m crs/claude-sonnet-4-5 "Hello, world!"
|
|
106
|
+
|
|
107
|
+
# Use with thinking budget variants
|
|
108
|
+
opencode run -m crs/claude-opus-4-5:low "Simple task" # 8K thinking tokens
|
|
109
|
+
opencode run -m crs/claude-opus-4-5:max "Complex task" # 32K thinking tokens
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Model Configuration
|
|
113
|
+
|
|
114
|
+
### Context Limits
|
|
115
|
+
|
|
116
|
+
- **Opus 4.5**: 200K context, 64K output tokens
|
|
117
|
+
- **Sonnet 4.5**: Default limits (inherited from SDK)
|
|
118
|
+
|
|
119
|
+
### Extended Thinking
|
|
120
|
+
|
|
121
|
+
Both models support extended thinking with configurable budgets via variants:
|
|
122
|
+
|
|
123
|
+
- **`:low`** - 8,192 thinking tokens for simpler tasks
|
|
124
|
+
- **`:max`** - 32,768 thinking tokens for complex reasoning
|
|
125
|
+
|
|
126
|
+
Use variants by appending `:variant` to the model ID (e.g., `crs/claude-opus-4-5:max`).
|
|
127
|
+
|
|
128
|
+
### Modalities
|
|
129
|
+
|
|
130
|
+
All models support:
|
|
131
|
+
- **Input**: text, images, PDF documents
|
|
132
|
+
- **Output**: text
|
|
133
|
+
|
|
134
|
+
## Supported Models
|
|
135
|
+
|
|
136
|
+
Models available through CRS/Bedrock:
|
|
137
|
+
|
|
138
|
+
| Model ID | Bedrock ID |
|
|
139
|
+
|----------|------------|
|
|
140
|
+
| `claude-sonnet-4-20250514` | `us.anthropic.claude-sonnet-4-20250514-v1:0` |
|
|
141
|
+
| `claude-opus-4-20250514` | `us.anthropic.claude-opus-4-20250514-v1:0` |
|
|
142
|
+
| `claude-3-5-sonnet-20241022` | `us.anthropic.claude-3-5-sonnet-20241022-v2:0` |
|
|
143
|
+
| `claude-3-5-haiku-20241022` | `us.anthropic.claude-3-5-haiku-20241022-v1:0` |
|
|
144
|
+
| `claude-3-opus-20240229` | `us.anthropic.claude-3-opus-20240229-v1:0` |
|
|
145
|
+
|
|
146
|
+
## Debugging
|
|
147
|
+
|
|
148
|
+
Enable debug logging to see SSE transformation details:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
CRS_DEBUG_SSE=true opencode
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Example output:
|
|
155
|
+
```
|
|
156
|
+
[CRS-SSE] Event: message_start Data: {"type":"message","id":"msg_123"...}
|
|
157
|
+
[CRS-SSE] Event: content_block_delta Data: {"index":1,"delta":{"type":"input_json_delta"...}}
|
|
158
|
+
[CRS-SSE] Buffering tool delta, accumulated: {"query": "bitcoin price"
|
|
159
|
+
[CRS-SSE] Flushing pending tool block 1 with inferred name: websearch
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## How Tool Name Inference Works
|
|
163
|
+
|
|
164
|
+
CRS/Bedrock doesn't send `content_block_start` for tool calls, so the plugin:
|
|
165
|
+
|
|
166
|
+
1. **Detects tool deltas** - When `input_json_delta` arrives without a prior `content_block_start`
|
|
167
|
+
2. **Buffers deltas** - Accumulates the JSON until complete
|
|
168
|
+
3. **Infers tool name** - Matches accumulated JSON keys against tool schemas
|
|
169
|
+
4. **Emits events** - Sends `content_block_start` with inferred name, then buffered deltas
|
|
170
|
+
|
|
171
|
+
The inference scores each tool by:
|
|
172
|
+
- +1 for each matching parameter key
|
|
173
|
+
- -1 for each unknown key
|
|
174
|
+
- +10 bonus if all required parameters present
|
|
175
|
+
|
|
176
|
+
## Troubleshooting
|
|
177
|
+
|
|
178
|
+
### "Invalid input: expected array, received undefined"
|
|
179
|
+
|
|
180
|
+
This error means tool name inference failed. Enable debug logging to see which tool was inferred.
|
|
181
|
+
|
|
182
|
+
### API Key Format
|
|
183
|
+
|
|
184
|
+
CRS API keys start with `cr_`. The plugin uses the `x-api-key` header instead of the standard Anthropic auth.
|
|
185
|
+
|
|
186
|
+
### Connection Issues
|
|
187
|
+
|
|
188
|
+
1. Verify environment variables:
|
|
189
|
+
```bash
|
|
190
|
+
echo $ANTHROPIC_BASE_URL
|
|
191
|
+
echo $ANTHROPIC_AUTH_TOKEN
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
2. Test the endpoint:
|
|
195
|
+
```bash
|
|
196
|
+
curl -H "x-api-key: $ANTHROPIC_AUTH_TOKEN" "$ANTHROPIC_BASE_URL/v1/messages"
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
3. Enable debug logging:
|
|
200
|
+
```bash
|
|
201
|
+
CRS_DEBUG_SSE=true opencode run -m crs/claude-sonnet-4-5 "test"
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## License
|
|
205
|
+
|
|
206
|
+
MIT
|
package/bun.lock
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"lockfileVersion": 1,
|
|
3
|
+
"configVersion": 1,
|
|
4
|
+
"workspaces": {
|
|
5
|
+
"": {
|
|
6
|
+
"name": "opencode-crs-bedrock",
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"@opencode-ai/plugin": "*",
|
|
9
|
+
},
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
"packages": {
|
|
13
|
+
"@opencode-ai/plugin": ["@opencode-ai/plugin@1.1.23", "", { "dependencies": { "@opencode-ai/sdk": "1.1.23", "zod": "4.1.8" } }, "sha512-O/iLSKOUuzD95UWhj9y/tEuycPEBv36de0suHXXqeYLWZLZ16DAUSKR+YG7rvRjJS0sbn4biVMw+k7XXk/oxiQ=="],
|
|
14
|
+
|
|
15
|
+
"@opencode-ai/sdk": ["@opencode-ai/sdk@1.1.23", "", {}, "sha512-YjN9ogzkLol92s+/iARXRop9/5oFIezUkvWVay12u1IM6A/WJs50DeKl3oL0x4a68P1a5tI5gD98dLnk2+AlsA=="],
|
|
16
|
+
|
|
17
|
+
"zod": ["zod@4.1.8", "", {}, "sha512-5R1P+WwQqmmMIEACyzSvo4JXHY5WiAFHRMg+zBZKgKS+Q1viRa0C1hmUKtHltoIFKtIdki3pRxkmpP74jnNYHQ=="],
|
|
18
|
+
}
|
|
19
|
+
}
|