fix: set Bedrock streaming accept headers (#24776)

> Mux is working on behalf of Mike.

## Summary
- Bump `github.com/coder/anthropic-sdk-go` to the clean Bedrock
streaming header fix from coder/anthropic-sdk-go#10.
- Add chatd regression coverage that verifies Bedrock streaming requests
use AWS event stream headers and include `X-Amzn-Bedrock-Accept` in the
SigV4 signed headers.

## SDK follow-up
- Reverted the bad coder/anthropic-sdk-go#8 merge with
coder/anthropic-sdk-go#9.
- Re-applied only the intended Bedrock streaming header change in
coder/anthropic-sdk-go#10.

## Validation
- `go test ./coderd/x/chatd/chatprovider -run
'TestModelFromConfig_Bedrock(StreamingHeaders|StripsAnthropicHeaders)?$'
-count=1`
- `go test ./coderd/x/chatd/chatprovider -count=1`
- `go mod tidy -diff`
- `make lint`
- pre-commit hook during `git commit`
This commit is contained in:
Michael Suchacz
2026-04-28 11:28:20 +00:00
committed by GitHub
parent 411dc1ca8e
commit dec3e98e54
3 changed files with 85 additions and 4 deletions
@@ -1049,6 +1049,87 @@ func TestModelFromConfig_BedrockStripsAnthropicHeaders(t *testing.T) {
require.Contains(t, got.Body, `"anthropic_version":"bedrock-2023-05-31"`)
}
func TestModelFromConfig_BedrockStreamingHeaders(t *testing.T) {
ctx := testutil.Context(t, testutil.WaitShort)
t.Setenv("ANTHROPIC_API_KEY", "anthropic-env-key")
t.Setenv("AWS_REGION", "us-east-2")
t.Setenv("AWS_ACCESS_KEY_ID", "test-access-key")
t.Setenv("AWS_SECRET_ACCESS_KEY", "test-secret-key")
t.Setenv("AWS_SESSION_TOKEN", "test-session-token")
type requestCapture struct {
Path string
Accept string
BedrockAccept string
Authorization string
Body string
ReadError error
}
requests := make(chan requestCapture, 1)
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(r.Body)
requests <- requestCapture{
Path: r.URL.Path,
Accept: r.Header.Get("Accept"),
BedrockAccept: r.Header.Get("X-Amzn-Bedrock-Accept"),
Authorization: r.Header.Get("Authorization"),
Body: string(body),
ReadError: err,
}
w.Header().Set("Content-Type", "application/vnd.amazon.eventstream")
w.WriteHeader(http.StatusOK)
}))
defer server.Close()
model, err := chatprovider.ModelFromConfig(
fantasybedrock.Name,
"anthropic.claude-opus-4-6-v1",
chatprovider.ProviderAPIKeys{
ByProvider: map[string]string{
fantasybedrock.Name: "",
},
BaseURLByProvider: map[string]string{
fantasybedrock.Name: server.URL,
},
},
chatprovider.UserAgent(),
nil,
nil,
)
require.NoError(t, err)
require.NotNil(t, model)
stream, err := model.Stream(ctx, fantasy.Call{
Prompt: []fantasy.Message{
{
Role: fantasy.MessageRoleUser,
Content: []fantasy.MessagePart{
fantasy.TextPart{Text: "hello"},
},
},
},
})
require.NoError(t, err)
for part := range stream {
require.NotEqual(t, fantasy.StreamPartTypeError, part.Type)
break
}
got := testutil.TryReceive(ctx, t, requests)
require.NoError(t, got.ReadError)
require.Equal(t, "/model/us.anthropic.claude-opus-4-6-v1/invoke-with-response-stream", got.Path)
require.Equal(t, "application/vnd.amazon.eventstream", got.Accept)
require.Equal(t, "application/json", got.BedrockAccept)
require.Contains(t, got.Authorization, "AWS4-HMAC-SHA256")
require.Contains(t, got.Authorization, "x-amzn-bedrock-accept")
require.Contains(t, got.Body, `"anthropic_version":"bedrock-2023-05-31"`)
}
func bedrockNonStreamingResponse() map[string]any {
return map[string]any{
"id": "msg_01Test",
+2 -2
View File
@@ -91,8 +91,8 @@ replace charm.land/fantasy => github.com/coder/fantasy v0.0.0-20260426185602-951
// coder/coder uses a fork of charmbracelet's fork of the Anthropic Go SDK
// with performance improvements and Bedrock header cleanup.
// See: https://github.com/coder/anthropic-sdk-go/commits/3be8e193ec89
replace github.com/charmbracelet/anthropic-sdk-go => github.com/coder/anthropic-sdk-go v0.0.0-20260424230212-3be8e193ec89
// See: https://github.com/coder/anthropic-sdk-go/commits/67514346e0fb
replace github.com/charmbracelet/anthropic-sdk-go => github.com/coder/anthropic-sdk-go v0.0.0-20260428111347-67514346e0fb
// Replace sdks with our own optimized forks until relevant upstream PRs are merged.
// https://github.com/anthropics/anthropic-sdk-go/pull/262
+2 -2
View File
@@ -314,8 +314,8 @@ github.com/coder/agentapi-sdk-go v0.0.0-20250505131810-560d1d88d225 h1:tRIViZ5JR
github.com/coder/agentapi-sdk-go v0.0.0-20250505131810-560d1d88d225/go.mod h1:rNLVpYgEVeu1Zk29K64z6Od8RBP9DwqCu9OfCzh8MR4=
github.com/coder/aisdk-go v0.0.9 h1:Vzo/k2qwVGLTR10ESDeP2Ecek1SdPfZlEjtTfMveiVo=
github.com/coder/aisdk-go v0.0.9/go.mod h1:KF6/Vkono0FJJOtWtveh5j7yfNrSctVTpwgweYWSp5M=
github.com/coder/anthropic-sdk-go v0.0.0-20260424230212-3be8e193ec89 h1:IVJutHfU944mb4D66K7XdPwKMAJrNC9FOq6JB4bveuI=
github.com/coder/anthropic-sdk-go v0.0.0-20260424230212-3be8e193ec89/go.mod h1:hqlYqR7uPKOKfnNeicUbZp0Ps0GeYFlKYtwh5HGDCx8=
github.com/coder/anthropic-sdk-go v0.0.0-20260428111347-67514346e0fb h1:/qJzrmYz+ii329dgoQ1BxtlrhdJvtTkk7UwhrroSohU=
github.com/coder/anthropic-sdk-go v0.0.0-20260428111347-67514346e0fb/go.mod h1:hqlYqR7uPKOKfnNeicUbZp0Ps0GeYFlKYtwh5HGDCx8=
github.com/coder/boundary v0.8.4-0.20260304164748-566aeea939ab h1:HrlxyTmMQpOHfSKzRU1vf5TxrmV6vL5OiWq+Dvn5qh0=
github.com/coder/boundary v0.8.4-0.20260304164748-566aeea939ab/go.mod h1:BhJhyKW/+zZQzaGZ3vn27if2k0Vx5xLXzq7ZCQx5gPk=
github.com/coder/bubbletea v1.2.2-0.20241212190825-007a1cdb2c41 h1:SBN/DA63+ZHwuWwPHPYoCZ/KLAjHv5g4h2MS4f2/MTI=