feat(llm): add Bedrock Converse adapter

Implements the AWS Bedrock Converse streaming protocol as the 5th
first-class adapter in @opencode-ai/llm. Single `bedrock-converse`
adapter covers all underlying models (Anthropic, Llama, Mistral,
Cohere, Nova, Titan) since Converse is uniform.

Wire format: messages with text / reasoning / toolUse / toolResult
content blocks, system blocks, inferenceConfig, toolConfig with
toolSpec + toolChoice. Image / document / cache-point content types
are still TODO.

Streaming: AWS event stream binary framing via @smithy/eventstream-codec.
Each frame is decoded then dispatched by `:event-type` header into
the chunk schema. Bedrock splits the finish across `messageStop`
(reason) and `metadata` (usage) — the parser stashes the reason and
emits a single consolidated `request-finish` event when metadata
arrives, with an `onHalt` fallback for truncated streams.

Auth: two paths. Bearer API key (newer) when the consumer sets
`model.headers.authorization = 'Bearer <key>'`. SigV4 signing via
aws4fetch otherwise — credentials live on `model.native.aws_credentials`
and are signed at `toHttp` time so STS-vended tokens are picked up
when the consumer rebuilds the model. The adapter rejects requests
with neither auth path with a clear InvalidRequestError.

Routing: `@ai-sdk/amazon-bedrock` lowers to `bedrock-converse` via
the new `AmazonBedrock` provider routing module; the OpenCode
`llm-bridge.ts` registers it.

Cassette format: response bodies under
`application/vnd.amazon.eventstream` and `application/octet-stream`
content types are now stored as base64 with `bodyEncoding: 'base64'`
on the response snapshot — text round-tripping mangled the CRC32
fields in event-stream frames. Existing cassettes (SSE/JSON) omit
the field and decode as text unchanged.

Tests: 11 deterministic fixtures (prepare / lower messages / lower
tool config / decode text+usage / decode tool calls / decode
reasoning / decode throttling exception / auth path validation /
SigV4 plumbing) + 2 recorded cassettes against live Bedrock
(`us.amazon.nova-micro-v1:0` in us-east-1) for streaming text and
streaming tool calls.

AGENTS.md: documents the Bedrock auth model, binary cassette format,
and updates the protocol coverage / cassette backlog.

Deps: @smithy/eventstream-codec, @smithy/util-utf8, aws4fetch (~40KB
combined; matches AI SDK's approach).
This commit is contained in:
Kit Langton
2026-04-26 19:41:59 -04:00
parent 6c887b0faa
commit 769d6123d5
11 changed files with 1163 additions and 10 deletions
@@ -1,4 +1,5 @@
import * as LLM from "@opencode-ai/llm/llm"
import { AmazonBedrock } from "@opencode-ai/llm/provider/amazon-bedrock"
import { Anthropic } from "@opencode-ai/llm/provider/anthropic"
import { Azure } from "@opencode-ai/llm/provider/azure"
import { GitHubCopilot } from "@opencode-ai/llm/provider/github-copilot"
@@ -18,6 +19,7 @@ type Input = {
}
const PROVIDERS: Record<string, ProviderDefinition> = {
"@ai-sdk/amazon-bedrock": AmazonBedrock.provider,
"@ai-sdk/anthropic": Anthropic.provider,
"@ai-sdk/azure": Azure.provider,
"@ai-sdk/baseten": OpenAICompatibleFamily.provider,