Last updated 2026-05-17

AWS Bedrock Provider

The bedrock provider talks to AWS Bedrock's bedrock-runtime endpoint, with full SigV4 request signing and per-model-family body translation. Chat is exposed via Provider; embeddings via Embedder (Titan and Cohere on Bedrock). TTS and STT are not supported on Bedrock.

Import path

import (
"github.com/elloloop/llmrouter"
"github.com/elloloop/llmrouter/providers/bedrock"
)

Construction

Bedrock uses AWS SigV4 — there is no API key. The provider takes an aws.Config and a region:

import (
"github.com/aws/aws-sdk-go-v2/config"
"github.com/elloloop/llmrouter/providers/bedrock"
)
awsCfg, err := config.LoadDefaultConfig(ctx, config.WithRegion("us-east-1"))
if err != nil {
log.Fatal(err)
}
p, err := bedrock.New(
bedrock.WithAWSConfig(awsCfg),
bedrock.WithRegion("us-east-1"),
)

Credentials are resolved by the AWS SDK chain (env vars, shared credentials file, EC2/ECS/EKS metadata, SSO). The library never sees raw credentials.

Model routing and body translation

Bedrock hosts many vendor families on one runtime, each with its own request body. The provider inspects ChatRequest.Model and routes to the right body translator:

Model prefix Family Body shape
anthropic.claude-*ClaudeAnthropic /v1/messages
meta.llama*LlamaMeta chat templating
amazon.titan-*TitanAmazon inputText
amazon.nova-*NovaAmazon Converse
mistral.*MistralMistral chat
cohere.command-*CohereCohere /v1/chat

The response stream is translated back to OpenAI-shaped Chunks in every case.

Chat example (Claude on Bedrock)

stream, err := p.CompletionStream(ctx, llmrouter.ChatRequest{
Model: "anthropic.claude-3-5-sonnet-20241022-v2:0",
Messages: []llmrouter.Message{
llmrouter.TextMessage("system", "You are concise."),
llmrouter.TextMessage("user", "What's interesting about SigV4?"),
},
MaxTokens: 256,
})
if err != nil {
log.Fatal(err)
}
for chunk := range stream.Chunks() {
for _, c := range chunk.Choices {
fmt.Print(c.Delta.Content)
}
}
if err := stream.Err(); err != nil {
log.Fatal(err)
}

Embeddings (Titan, Cohere)

Bedrock exposes two embedding families. The library routes EmbedRequest.Model the same way as chat:

  • amazon.titan-embed-text-v2:0 — 1024 dims, Amazon Titan.
  • amazon.titan-embed-text-v1 — 1536 dims, legacy.
  • cohere.embed-english-v3 — 1024 dims, Cohere on Bedrock.
  • cohere.embed-multilingual-v3 — 1024 dims.
resp, err := p.Embed(ctx, llmrouter.EmbedRequest{
Model: "amazon.titan-embed-text-v2:0",
Inputs: []string{"What is SigV4?"},
TaskType: "RETRIEVAL_DOCUMENT", // mapped to Cohere input_type when applicable
})

Error handling

Non-2xx responses surface as *llmrouter.ErrUpstream with Provider == "bedrock". Common cases:

  • 403 — IAM principal lacks bedrock:InvokeModel* for the model ARN.
  • 400 — model not enabled in the region (request access in the Bedrock console).
  • 429 — service quota exhausted; either request a quota increase or use Provisioned Throughput.

Caveats

  • No TTS, no STT. Bedrock has Polly (TTS) and Transcribe (STT) as separate AWS services; llmrouter does not wrap them. Use the AWS SDKs directly or ElevenLabs / Cartesia / Deepgram.
  • Region matters. Model availability varies by region; us-east-1 has the broadest catalogue.
  • Cross-region inference profiles. Some newer models (Claude 3.5 Sonnet v2) are accessed via us.anthropic.claude-3-5-sonnet-20241022-v2:0 profile IDs. Pass them as-is.

See also