feat(llm): Amazon Bedrock provider over a standard-library SigV4 signer

Kind `bedrock` speaks the Converse API for chat roles and InvokeModel for
Titan and Cohere embeddings. Requests are signed with AWS Signature
Version 4 implemented with crypto/hmac and crypto/sha256 only; the signer
is pinned by tests to signatures botocore produces for the same requests,
with and without a session token, so drift fails the build.

The credential is one secret reference, ACCESS_KEY_ID:SECRET_ACCESS_KEY
[:SESSION_TOKEN], encrypted together like every other provider key. The
region is read from the endpoint host, which must be a Bedrock runtime
host over HTTPS unless private endpoints are explicitly allowed; other
hosts are refused. Provider errors are scrubbed of the secret and token.

Migration 0024 widens the provider kind constraint; existing rows are
untouched and the down migration refuses while bedrock rows exist rather
than dropping configuration. UI kind selects, docs/llm-providers.md,
ADR-0010, ARCHITECTURE and the changelog are updated.
This commit is contained in:
Andy Dixon
2026-08-24 22:03:48 +01:00
parent 7d608c2783
commit f165adc5f2
14 changed files with 497 additions and 6 deletions
+7
View File
@@ -157,6 +157,8 @@ func (r *Router) build(c routing, m Model) (llm.LanguageModel, error) {
return providers.NewAnthropic(cfg), nil
case llm.KindOllama:
return providers.NewOllama(cfg), nil
case llm.KindBedrock:
return providers.NewBedrock(cfg)
}
return nil, fmt.Errorf("provider %s: unsupported kind %s", p.Name, p.Kind)
}
@@ -187,6 +189,11 @@ func (r *Router) Embedder(ctx context.Context) embeddings.Embedder {
switch p.Kind {
case llm.KindOllama:
return providers.NewOllamaEmbedder(cfg, m.Dims)
case llm.KindBedrock:
if e, err := providers.NewBedrockEmbedder(cfg, m.Dims); err == nil {
return e
}
return embeddings.None{}
case llm.KindOpenAI:
return providers.NewOpenAIEmbedder(cfg, m.Dims)
}