Merge pull request #2751 from wucm667/fix/bedrock-strip-context-management-when-beta-removed
fix(bedrock): v0.1.130 回归 — beta token 被移除时同步剥离 context_management 字段
This commit is contained in:
commit
b9f421d647
@ -185,6 +185,7 @@ func BuildBedrockURL(region, modelID string, stream bool) string {
|
||||
// 5. 清理 cache_control 中 Bedrock 不支持的字段(scope, ttl)
|
||||
// 6. 修复 thinking 字段兼容性(Opus 4.7 仅支持 adaptive,enabled 需要 budget_tokens)
|
||||
// 7. 清理 tool_use.id / tool_use_id 中 Bedrock 不接受的字符
|
||||
// 8. 根据最终 Bedrock beta tokens 剥离不再支持的 beta 字段
|
||||
func PrepareBedrockRequestBody(body []byte, modelID string, betaHeader string) ([]byte, error) {
|
||||
betaTokens := ResolveBedrockBetaTokens(betaHeader, body, modelID)
|
||||
return PrepareBedrockRequestBodyWithTokens(body, modelID, betaTokens, false)
|
||||
@ -195,6 +196,9 @@ func PrepareBedrockRequestBody(body []byte, modelID string, betaHeader string) (
|
||||
func PrepareBedrockRequestBodyWithTokens(body []byte, modelID string, betaTokens []string, ccCompat bool) ([]byte, error) {
|
||||
var err error
|
||||
|
||||
betaTokens = filterBedrockBetaTokens(betaTokens)
|
||||
body = sanitizeBedrockFieldsForBetaTokens(body, betaTokens)
|
||||
|
||||
// 注入 anthropic_version(Bedrock 要求)
|
||||
body, err = sjson.SetBytes(body, "anthropic_version", "bedrock-2023-05-31")
|
||||
if err != nil {
|
||||
@ -471,6 +475,8 @@ var bedrockSupportedBetaTokens = map[string]bool{
|
||||
"tool-examples-2025-10-29": true,
|
||||
}
|
||||
|
||||
const bedrockContextManagementBetaToken = "context-management-2025-06-27"
|
||||
|
||||
// bedrockBetaTokenTransforms 定义 Bedrock Invoke 特有的 beta 头转换规则
|
||||
// Anthropic 直接 API 使用通用头,Bedrock Invoke 需要特定的替代头
|
||||
var bedrockBetaTokenTransforms = map[string]string{
|
||||
@ -617,6 +623,22 @@ func filterBedrockBetaTokens(tokens []string) []string {
|
||||
return result
|
||||
}
|
||||
|
||||
func sanitizeBedrockFieldsForBetaTokens(body []byte, betaTokens []string) []byte {
|
||||
if !containsBedrockBetaToken(betaTokens, bedrockContextManagementBetaToken) && gjson.GetBytes(body, "context_management").Exists() {
|
||||
body, _ = sjson.DeleteBytes(body, "context_management")
|
||||
}
|
||||
return body
|
||||
}
|
||||
|
||||
func containsBedrockBetaToken(tokens []string, target string) bool {
|
||||
for _, token := range tokens {
|
||||
if token == target {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// bedrockToolUseIDRe 匹配 Bedrock 允许的 tool_use ID 字符(字母、数字、下划线、连字符)
|
||||
var bedrockToolUseIDRe = regexp.MustCompile(`[^a-zA-Z0-9_-]`)
|
||||
|
||||
|
||||
@ -378,6 +378,67 @@ func TestPrepareBedrockRequestBody_BetaFiltering(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestPrepareBedrockRequestBodyWithTokens_ContextManagementRequiresSupportedBeta(t *testing.T) {
|
||||
modelID := "us.anthropic.claude-opus-4-6-v1"
|
||||
|
||||
t.Run("strips context_management when final tokens omit context-management beta", func(t *testing.T) {
|
||||
input := `{
|
||||
"messages":[{"role":"user","content":"hi"}],
|
||||
"max_tokens":100,
|
||||
"context_management":{"edits":[{"type":"clear_thinking_20251015","keep":"all"}]}
|
||||
}`
|
||||
betaTokens := []string{"context-1m-2025-08-07"}
|
||||
originalTokens := append([]string(nil), betaTokens...)
|
||||
|
||||
result, err := PrepareBedrockRequestBodyWithTokens([]byte(input), modelID, betaTokens, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.False(t, gjson.GetBytes(result, "context_management").Exists())
|
||||
assert.Equal(t, originalTokens, betaTokens)
|
||||
assert.Equal(t, originalTokens, bedrockAnthropicBetaNames(result))
|
||||
})
|
||||
|
||||
t.Run("leaves body without context_management otherwise intact", func(t *testing.T) {
|
||||
input := `{"messages":[{"role":"user","content":"hi"}],"max_tokens":100}`
|
||||
|
||||
result, err := PrepareBedrockRequestBodyWithTokens([]byte(input), modelID, nil, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.False(t, gjson.GetBytes(result, "context_management").Exists())
|
||||
assert.False(t, gjson.GetBytes(result, "anthropic_beta").Exists())
|
||||
assert.Equal(t, "hi", gjson.GetBytes(result, "messages.0.content").String())
|
||||
assert.Equal(t, int64(100), gjson.GetBytes(result, "max_tokens").Int())
|
||||
})
|
||||
|
||||
t.Run("filters explicit unsupported context-management beta and strips field", func(t *testing.T) {
|
||||
input := `{
|
||||
"messages":[{"role":"user","content":"hi"}],
|
||||
"max_tokens":100,
|
||||
"context_management":{"edits":[{"type":"clear_thinking_20251015","keep":"all"}]}
|
||||
}`
|
||||
|
||||
result, err := PrepareBedrockRequestBodyWithTokens(
|
||||
[]byte(input),
|
||||
modelID,
|
||||
[]string{bedrockContextManagementBetaToken, "context-1m-2025-08-07"},
|
||||
false,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.False(t, gjson.GetBytes(result, "context_management").Exists())
|
||||
assert.Equal(t, []string{"context-1m-2025-08-07"}, bedrockAnthropicBetaNames(result))
|
||||
})
|
||||
}
|
||||
|
||||
func bedrockAnthropicBetaNames(body []byte) []string {
|
||||
arr := gjson.GetBytes(body, "anthropic_beta").Array()
|
||||
names := make([]string, len(arr))
|
||||
for i, token := range arr {
|
||||
names[i] = token.String()
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
func TestBedrockCrossRegionPrefix(t *testing.T) {
|
||||
tests := []struct {
|
||||
region string
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user