mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
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:
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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=
|
||||
|
||||
Reference in New Issue
Block a user