Following up on #50, where you said:
we don't yet have a dedicated built-in Bedrock preset (with native SigV4 auth, region selection, etc.) — if that's something you'd find valuable and are interested in contributing, we'd welcome a PR adding Bedrock as a first-class provider in internal/llm/providers.go. Happy to help scope it out in an issue if you'd like to discuss the approach first.
Taking you up on the scoping offer. I have this working and tested against a live Bedrock account, and there is one decision I would rather have your view on before opening a PR: the AWS SDK dependency.
The gap the existing paths leave
To be clear about what is already possible, since #50 covered it: the LiteLLM preset and the OpenAI-compatible endpoint both work. The case neither covers is an organisation whose Bedrock access is governed entirely through IAM and SSO.
- LiteLLM proxy means a process to run in front of every workstation and CI runner. Workable, but it is infrastructure to operate for what is otherwise a self-contained CLI.
- Custom provider against the OpenAI-compatible endpoint needs a Bedrock API key — a long-lived static credential to generate, distribute and rotate. That is exactly what organisations move to SSO to stop doing. Ours are short-lived credentials from
aws sso login, scoped by an IAM permission set; there is no key to put in a config file.
Native SigV4 removes both. ocr then authenticates the same way every other AWS tool on the machine already does, and access is granted or revoked by editing an IAM policy.
Shape of the change
Bedrock serves the same Messages API, so very little is new. bedrock.WithConfig from anthropic-sdk-go handles the signing, the model-into-path rewrite, and anthropic_version injection — and it returns an option.RequestOption, which is exactly the type NewAnthropicClient already assembles a slice of. So AnthropicClient is reused as-is behind a new protocol constant, following the extension contract protocol.go documents for itself.
protocol.go — one constant plus its NormalizeProtocol / ValidateProtocol entries
providers.go — a bedrock preset, and an AmbientAuth flag marking providers that authenticate from the environment rather than an api_key
resolver.go — two checks taught about ambient auth (below)
client.go — NewAnthropicBedrockClient, essentially the existing constructor minus the auth headers
- tests, including one asserting
api_key is still required for every non-ambient provider
The resolver changes are the part most likely to want your opinion. tryProviderConfig requires a non-empty api_key, and ResolveEndpointWithModelOverride requires both URL and Token before an endpoint counts as complete. Bedrock has none of the three — the region supplies the host, SigV4 supplies the credentials — so a correct config fell through every strategy and surfaced no valid LLM endpoint configured, i.e. the error for having configured nothing at all. I gated both on AmbientAuth rather than special-casing a provider name, but I am not attached to that shape.
Region and profile resolve from the standard AWS chain, with optional aws_region / aws_profile in the provider entry so a run is reproducible without exporting environment variables first.
The dependency question
It adds aws-sdk-go-v2/config as the only direct dependency, and 13 transitive modules with it (credentials, service/sso, service/ssooidc, service/sts, feature/ec2/imds, internal/ini, smithy-go, and a few smaller ones). That is real weight for a provider most users will not enable, and I would understand hesitation.
Options, happy to follow whichever you prefer:
- Direct dependency — simplest; one
go.mod entry and it works out of the box.
- Build tag —
//go:build bedrock, keeping the SDK out of default builds, at the cost of a second build configuration and a release-artefact decision.
- Separate module or plugin — cleanest dependency story, most work; I would want your guidance on placement given
extensions/ and plugins/.
I lean toward (1) for usability but will defer to whatever fits your release story. If the answer is (2) or (3), knowing now saves a rewrite.
One thing that may belong in the SDK instead
Worth surfacing because it affects scope. bedrock.WithConfig prefers bearer-token auth whenever cfg.BearerAuthTokenProvider is non-nil, and config.LoadDefaultConfig populates that from the AWS SSO token cache. So any SSO-authenticated caller silently sends its SSO OIDC access token instead of signing, and Bedrock answers:
403 Forbidden {"Message":"Invalid API Key format: Must start with pre-defined prefix"}
Diagnosis is slower than it should be because the SDK reports the pre-middleware URL (/v1/messages) in the error, which makes the path rewriting look broken when it is working correctly.
My patch clears that provider unless AWS_BEARER_TOKEN_BEDROCK was set deliberately. It works, but it is arguably a workaround for an anthropic-sdk-go bug. If you would rather see it fixed there first, that is fair and would shrink the ocr-side patch. I also noticed bedrock/bedrockmantle.go resolves auth by an explicit documented precedence, which reads as the more considered design of the two — possibly relevant if you have a view on which path a built-in preset should take.
Happy to open the PR as soon as you have a preference on the dependency, or to link the branch first if seeing the code would help you decide.
Following up on #50, where you said:
Taking you up on the scoping offer. I have this working and tested against a live Bedrock account, and there is one decision I would rather have your view on before opening a PR: the AWS SDK dependency.
The gap the existing paths leave
To be clear about what is already possible, since #50 covered it: the LiteLLM preset and the OpenAI-compatible endpoint both work. The case neither covers is an organisation whose Bedrock access is governed entirely through IAM and SSO.
aws sso login, scoped by an IAM permission set; there is no key to put in a config file.Native SigV4 removes both.
ocrthen authenticates the same way every other AWS tool on the machine already does, and access is granted or revoked by editing an IAM policy.Shape of the change
Bedrock serves the same Messages API, so very little is new.
bedrock.WithConfigfromanthropic-sdk-gohandles the signing, the model-into-path rewrite, andanthropic_versioninjection — and it returns anoption.RequestOption, which is exactly the typeNewAnthropicClientalready assembles a slice of. SoAnthropicClientis reused as-is behind a new protocol constant, following the extension contractprotocol.godocuments for itself.protocol.go— one constant plus itsNormalizeProtocol/ValidateProtocolentriesproviders.go— abedrockpreset, and anAmbientAuthflag marking providers that authenticate from the environment rather than anapi_keyresolver.go— two checks taught about ambient auth (below)client.go—NewAnthropicBedrockClient, essentially the existing constructor minus the auth headersapi_keyis still required for every non-ambient providerThe resolver changes are the part most likely to want your opinion.
tryProviderConfigrequires a non-emptyapi_key, andResolveEndpointWithModelOverriderequires bothURLandTokenbefore an endpoint counts as complete. Bedrock has none of the three — the region supplies the host, SigV4 supplies the credentials — so a correct config fell through every strategy and surfacedno valid LLM endpoint configured, i.e. the error for having configured nothing at all. I gated both onAmbientAuthrather than special-casing a provider name, but I am not attached to that shape.Region and profile resolve from the standard AWS chain, with optional
aws_region/aws_profilein the provider entry so a run is reproducible without exporting environment variables first.The dependency question
It adds
aws-sdk-go-v2/configas the only direct dependency, and 13 transitive modules with it (credentials,service/sso,service/ssooidc,service/sts,feature/ec2/imds,internal/ini,smithy-go, and a few smaller ones). That is real weight for a provider most users will not enable, and I would understand hesitation.Options, happy to follow whichever you prefer:
go.modentry and it works out of the box.//go:build bedrock, keeping the SDK out of default builds, at the cost of a second build configuration and a release-artefact decision.extensions/andplugins/.I lean toward (1) for usability but will defer to whatever fits your release story. If the answer is (2) or (3), knowing now saves a rewrite.
One thing that may belong in the SDK instead
Worth surfacing because it affects scope.
bedrock.WithConfigprefers bearer-token auth whenevercfg.BearerAuthTokenProvideris non-nil, andconfig.LoadDefaultConfigpopulates that from the AWS SSO token cache. So any SSO-authenticated caller silently sends its SSO OIDC access token instead of signing, and Bedrock answers:Diagnosis is slower than it should be because the SDK reports the pre-middleware URL (
/v1/messages) in the error, which makes the path rewriting look broken when it is working correctly.My patch clears that provider unless
AWS_BEARER_TOKEN_BEDROCKwas set deliberately. It works, but it is arguably a workaround for ananthropic-sdk-gobug. If you would rather see it fixed there first, that is fair and would shrink theocr-side patch. I also noticedbedrock/bedrockmantle.goresolves auth by an explicit documented precedence, which reads as the more considered design of the two — possibly relevant if you have a view on which path a built-in preset should take.Happy to open the PR as soon as you have a preference on the dependency, or to link the branch first if seeing the code would help you decide.