Add AWS Bedrock provider (Converse + ConverseStream)#461
Conversation
Bedrock's OpenAI-compatible endpoint only serves openai.* models; Claude and other inference profiles are reachable only through the native Converse API. This adds a dedicated `bedrock` API type implementing both Converse (non-streaming) and ConverseStream (binary event-stream framing), with bearer-token auth, tool calling and reasoning. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
fcee451 to
b20b2be
Compare
|
I wrote this with Claude Opus 4.7 and reviewed as well with OpenAI GPT 5.5 It works for my usecase, here's what I tested: Configuration. Bedrock configured as a standard provider entry: {:providers {"bedrock" {:api "bedrock"
:url "https://bedrock-runtime.ap-southeast-1.amazonaws.com"
:key "<bearer token>"
:models {"global.anthropic.claude-sonnet-4-6" {}}}}}
Proven working — live against AWS Bedrock:
Not covered:
If you think this is in the right direction I can try to test what was not covered yet. |
There was a problem hiding this comment.
Pull request overview
Adds first-class support for AWS Bedrock’s native runtime APIs (Converse + ConverseStream) as a new bedrock API/provider in ECA, enabling access to models/inference profiles that aren’t reachable via Bedrock’s OpenAI-compatible endpoint.
Changes:
- Implement
eca.llm-providers.bedrockincluding message normalization, tool-calling loops, and an AWSvnd.amazon.eventstreamframe decoder for streaming. - Wire the new
bedrockAPI into provider selection and/loginprovider configuration metadata. - Document configuration/schema updates and add a dedicated Bedrock test suite.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
src/eca/llm_providers/bedrock.clj |
New Bedrock provider implementation (Converse/ConverseStream + event-stream decoding + tools/reasoning support). |
test/eca/llm_providers/bedrock_test.clj |
Comprehensive tests for decoder, normalization, streaming/tool loops, and error handling. |
src/eca/llm_api.clj |
Register bedrock API handler for provider configs. |
src/eca/features/providers.clj |
Add “AWS Bedrock” provider label, login fields, and default provider config. |
docs/config/models.md |
Add Bedrock configuration documentation and example. |
docs/config.json |
Extend schema enum to allow "bedrock" as a provider API type. |
CHANGELOG.md |
Add unreleased changelog entry for the Bedrock provider. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| (defn ^:private read-fully | ||
| "Reads exactly `n` bytes from `is`. Returns the byte-array, nil on a clean | ||
| EOF before any byte was read, or throws on a truncated frame." | ||
| ^bytes [^InputStream is ^long n] | ||
| (let [buf (byte-array n) | ||
| first-read (.read is buf 0 n)] | ||
| (cond | ||
| (neg? first-read) nil | ||
| :else (loop [off (long first-read)] | ||
| (if (< off n) | ||
| (let [r (.read is buf off (- n off))] | ||
| (if (neg? r) | ||
| (throw (ex-info "Unexpected EOF in Bedrock event-stream frame" {})) | ||
| (recur (+ off (long r))))) | ||
| buf))))) | ||
|
|
| (shared/deep-merge | ||
| (assoc-some | ||
| {:messages messages | ||
| :inferenceConfig {:maxTokens (or max-output-tokens default-max-output-tokens)}} | ||
| :system (when-not (string/blank? instructions) [{:text instructions}]) | ||
| :toolConfig (->tool-config tools) | ||
| :additionalModelRequestFields (when reason? | ||
| {:reasoning_config {:type "enabled" | ||
| :budget_tokens default-reasoning-budget-tokens}})) | ||
| (select-keys extra-payload allowed-extra-payload-keys))) |
|
@ricardosllm At first glance looks good to me, LMK if you wanna check copilot comments or we are good to go, after release, we might wanna mention in #eca channel in Clojurians to let more people aware of this new integration |
Closes #254.
Adds a
bedrockprovider/API type using AWS Bedrock's native Converse and ConverseStream APIs.Why
Bedrock's OpenAI-compatible endpoint only serves
openai.*models — Claude (and other) inference profiles return 404 there, so the existing OpenAI-compat custom-provider path can't reach them. This adds a dedicated handler. (Also supersedes the partial workaround discussed in #329.)Scope
vnd.amazon.eventstreamframe decoderAuthorization: Bearer <token>) — sidesteps SigV4; pairs withAWS_BEARER_TOKEN_BEDROCKtoolConfig/toolSpec/toolUse/toolResultblocksreasoningContentblocks;reasoning_configviaadditionalModelRequestFieldswhenreason?extraPayloadis filtered to Converse-valid top-level keys (verified against the botocore service model) so variant payloads aimed at other APIs don't 400SigV4 auth and a model-catalog endpoint are intentionally out of scope for this first cut.
Config
{ "providers": { "bedrock": { "api": "bedrock", "url": "https://bedrock-runtime.us-east-1.amazonaws.com", "key": "${env:AWS_BEARER_TOKEN_BEDROCK}", "models": { "us.anthropic.claude-sonnet-4-5-20250929-v1:0": {} } } } }Also reachable via the
/loginflow (providerbedrock).How to verify / test
Automated —
bb test(suite green,523 tests, 2881 assertions). Bedrock-specific:clojure -M:test --focus eca.llm-providers.bedrock-testcovers the binary event-stream decoder (crafted frames, truncated/malformed), message normalization,extraPayloadfiltering, request/response shapes, both tool-call loops, streaming callbacks, and error paths.Manual against live AWS:
export AWS_BEARER_TOKEN_BEDROCK=...bb debug-cli→./eca/login→bedrock../eca, select abedrock/...model, and run a chat. Verify:This branch was integrated and smoke-tested live (non-streaming text, ConverseStream streaming, multi-turn tool-call round-trip) by a downstream consumer that embeds ECA as a library.
🤖 Generated with Claude Code