Merge pull request #965 from bensonz/main [skip ci]

fix: Bedrock compatibility — 修复了 bedrock 不能用的问题. cache_control sanitization, Opus 4.6 mapping, [1m] suffix stripping
This commit is contained in:
Wesley Liddick
2026-02-24 08:45:36 +08:00
committed by GitHub

View File

@@ -436,8 +436,15 @@ class BedrockRelayService {
// 将标准Claude模型名映射为Bedrock格式
_mapToBedrockModel(modelName) {
// Strip [1m] suffix (long context variant) — Bedrock uses the same model ID
// but supports 1M context natively for models that have it
const cleanModelName = modelName.replace(/\[1m\]$/, '')
// 标准Claude模型名到Bedrock模型名的映射表
const modelMapping = {
// Claude Opus 4.6
'claude-opus-4-6': 'global.anthropic.claude-opus-4-6-v1',
// Claude 4.5 Opus
'claude-opus-4-5': 'us.anthropic.claude-opus-4-5-20251101-v1:0',
'claude-opus-4-5-20251101': 'us.anthropic.claude-opus-4-5-20251101-v1:0',
@@ -482,21 +489,21 @@ class BedrockRelayService {
// 如果已经是Bedrock格式直接返回
// Bedrock模型格式{region}.anthropic.{model-name} 或 anthropic.{model-name}
if (modelName.includes('.anthropic.') || modelName.startsWith('anthropic.')) {
return modelName
if (cleanModelName.includes('.anthropic.') || cleanModelName.startsWith('anthropic.')) {
return cleanModelName
}
// 查找映射
const mappedModel = modelMapping[modelName]
const mappedModel = modelMapping[cleanModelName]
if (mappedModel) {
return mappedModel
}
// 如果没有找到映射,返回原始模型名(可能会导致错误,但保持向后兼容)
logger.warn(`⚠️ 未找到模型映射: ${modelName},使用原始模型名`, {
logger.warn(`⚠️ 未找到模型映射: ${cleanModelName},使用原始模型名`, {
metadata: { originalModel: modelName }
})
return modelName
return cleanModelName
}
// 选择使用的区域
@@ -514,6 +521,35 @@ class BedrockRelayService {
return this.defaultRegion
}
// Sanitize cache_control fields for Bedrock compatibility.
// Bedrock only supports { type: "ephemeral" } — extra fields like "scope"
// (added in Claude Code v2.1.38+) cause ValidationException.
_sanitizeCacheControl(obj) {
if (obj === null || obj === undefined || typeof obj !== 'object') {
return obj
}
if (Array.isArray(obj)) {
obj.forEach((item) => this._sanitizeCacheControl(item))
return obj
}
if (obj.cache_control && typeof obj.cache_control === 'object') {
// Keep only the "type" field that Bedrock accepts
obj.cache_control = { type: obj.cache_control.type || 'ephemeral' }
}
// Recurse into known nested structures (messages[].content, tool input_schema, etc.)
for (const key of Object.keys(obj)) {
const val = obj[key]
if (val && typeof val === 'object') {
this._sanitizeCacheControl(val)
}
}
return obj
}
// 转换Claude格式请求到Bedrock格式
_convertToBedrockFormat(requestBody) {
const bedrockPayload = {
@@ -553,6 +589,9 @@ class BedrockRelayService {
bedrockPayload.tool_choice = requestBody.tool_choice
}
// Sanitize cache_control for Bedrock compatibility (strip unsupported fields like "scope")
this._sanitizeCacheControl(bedrockPayload)
return bedrockPayload
}