mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-07 13:18:18 +08:00
feat: 适配 sonnet5
This commit is contained in:
@@ -136,6 +136,7 @@ var DefaultBedrockModelMapping = map[string]string{
|
||||
"claude-opus-4-1": "us.anthropic.claude-opus-4-1-20250805-v1:0",
|
||||
"claude-opus-4-20250514": "us.anthropic.claude-opus-4-20250514-v1:0",
|
||||
// Claude Sonnet
|
||||
"claude-sonnet-5": "us.anthropic.claude-sonnet-5-v1",
|
||||
"claude-sonnet-4-6-thinking": "us.anthropic.claude-sonnet-4-6",
|
||||
"claude-sonnet-4-6": "us.anthropic.claude-sonnet-4-6",
|
||||
"claude-sonnet-4-5": "us.anthropic.claude-sonnet-4-5-20250929-v1:0",
|
||||
|
||||
@@ -28,8 +28,8 @@ import (
|
||||
// user-authored prose like "Today is foo." or "His date is 2026-06-30." from
|
||||
// being touched.
|
||||
var (
|
||||
datelineRegexHyphen = regexp.MustCompile("Today(['’ʼʹ])s date is (\\d{4})-(\\d{2})-(\\d{2})\\.")
|
||||
datelineRegexSlash = regexp.MustCompile("Today(['’ʼʹ])s date is (\\d{4})/(\\d{2})/(\\d{2})\\.")
|
||||
datelineRegexHyphen = regexp.MustCompile(`Today(['’ʼʹ])s date is (\d{4})-(\d{2})-(\d{2})\.`)
|
||||
datelineRegexSlash = regexp.MustCompile(`Today(['’ʼʹ])s date is (\d{4})/(\d{2})/(\d{2})\.`)
|
||||
)
|
||||
|
||||
// systemReminderRegex matches a <system-reminder> block. The dateline lives in
|
||||
@@ -66,10 +66,10 @@ func apostropheVariant(r rune) string {
|
||||
}
|
||||
|
||||
type datelineMatch struct {
|
||||
start, end int
|
||||
apoRune rune
|
||||
sep string
|
||||
year, month, day string
|
||||
start, end int
|
||||
apoRune rune
|
||||
sep string
|
||||
year, month, day string
|
||||
}
|
||||
|
||||
func collectMatches(text string, re *regexp.Regexp, sep string) []datelineMatch {
|
||||
@@ -124,8 +124,8 @@ func NormalizeText(text string) (string, []DatelineHit) {
|
||||
// Already canonical: no rewrite, no hit.
|
||||
continue
|
||||
}
|
||||
b.WriteString(text[prev:m.start])
|
||||
b.WriteString(canonical)
|
||||
_, _ = b.WriteString(text[prev:m.start])
|
||||
_, _ = b.WriteString(canonical)
|
||||
prev = m.end
|
||||
changed = true
|
||||
hits = append(hits, DatelineHit{
|
||||
@@ -136,7 +136,7 @@ func NormalizeText(text string) (string, []DatelineHit) {
|
||||
if !changed {
|
||||
return text, nil
|
||||
}
|
||||
b.WriteString(text[prev:])
|
||||
_, _ = b.WriteString(text[prev:])
|
||||
return b.String(), hits
|
||||
}
|
||||
|
||||
@@ -159,20 +159,20 @@ func normalizeSystemReminderScopedText(text string) (string, []DatelineHit) {
|
||||
var hits []DatelineHit
|
||||
changed := false
|
||||
for _, loc := range locs {
|
||||
b.WriteString(text[prev:loc[0]])
|
||||
_, _ = b.WriteString(text[prev:loc[0]])
|
||||
block := text[loc[0]:loc[1]]
|
||||
normalized, blockHits := NormalizeText(block)
|
||||
if normalized != block {
|
||||
changed = true
|
||||
}
|
||||
b.WriteString(normalized)
|
||||
_, _ = b.WriteString(normalized)
|
||||
hits = append(hits, blockHits...)
|
||||
prev = loc[1]
|
||||
}
|
||||
if !changed {
|
||||
return text, nil
|
||||
}
|
||||
b.WriteString(text[prev:])
|
||||
_, _ = b.WriteString(text[prev:])
|
||||
return b.String(), hits
|
||||
}
|
||||
|
||||
|
||||
@@ -146,6 +146,12 @@ var DefaultModels = []Model{
|
||||
DisplayName: "Claude Opus 4.8",
|
||||
CreatedAt: "2026-05-29T00:00:00Z",
|
||||
},
|
||||
{
|
||||
ID: "claude-sonnet-5",
|
||||
Type: "model",
|
||||
DisplayName: "Claude Sonnet 5",
|
||||
CreatedAt: "2026-07-01T00:00:00Z",
|
||||
},
|
||||
{
|
||||
ID: "claude-sonnet-4-6",
|
||||
Type: "model",
|
||||
|
||||
@@ -242,3 +242,72 @@ func TestIsCountTokensUnsupported404(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestDefaultBetaPolicy_Context1M_Sonnet5Whitelist 验证默认策略下 context-1m-2025-08-07 的分模型行为:
|
||||
// - claude-sonnet-5 及后续版本:pass(放行),保留 1M 上下文能力
|
||||
// - 其他 sonnet 版本(4.x 及以下)、opus、haiku:filter(过滤),因为上游不支持
|
||||
func TestDefaultBetaPolicy_Context1M_Sonnet5Whitelist(t *testing.T) {
|
||||
settings := DefaultBetaPolicySettings()
|
||||
|
||||
// 找到 context-1m-2025-08-07 规则
|
||||
var rule *BetaPolicyRule
|
||||
for i := range settings.Rules {
|
||||
if settings.Rules[i].BetaToken == "context-1m-2025-08-07" {
|
||||
rule = &settings.Rules[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
require.NotNil(t, rule, "default policy must include context-1m-2025-08-07 rule")
|
||||
require.Equal(t, BetaPolicyActionPass, rule.Action, "primary action for whitelisted models is pass")
|
||||
require.Equal(t, BetaPolicyActionFilter, rule.FallbackAction, "non-whitelisted models must be filtered")
|
||||
require.NotEmpty(t, rule.ModelWhitelist, "context-1m must be scoped to sonnet-5+ via whitelist")
|
||||
|
||||
// 表驱动:模型 → 期望 action
|
||||
// 覆盖每种上游路径下的模型 ID 变形:直连 Anthropic API、Vertex AI("@YYYYMMDD" 后缀)、
|
||||
// AWS Bedrock 跨区域推理(us./eu./apac./jp./au./us-gov./global./anthropic. 前缀)。
|
||||
cases := []struct {
|
||||
model string
|
||||
wantAction string
|
||||
desc string
|
||||
}{
|
||||
// —— 直连 Anthropic API —— sonnet-5 系列应放行
|
||||
{"claude-sonnet-5", BetaPolicyActionPass, "sonnet-5 canonical"},
|
||||
{"claude-sonnet-5-20260701", BetaPolicyActionPass, "sonnet-5 dated variant matches wildcard"},
|
||||
{"claude-sonnet-5-thinking", BetaPolicyActionPass, "sonnet-5 thinking variant matches wildcard"},
|
||||
// —— Vertex AI 归一化后的 sonnet-5 —— 也应放行
|
||||
{"claude-sonnet-5@20260701", BetaPolicyActionPass, "sonnet-5 Vertex-normalized dated form"},
|
||||
// —— AWS Bedrock 各跨区域前缀 sonnet-5 —— 也应放行
|
||||
{"us.anthropic.claude-sonnet-5-v1", BetaPolicyActionPass, "bedrock us. sonnet-5"},
|
||||
{"eu.anthropic.claude-sonnet-5-20260701-v1:0", BetaPolicyActionPass, "bedrock eu. sonnet-5 dated"},
|
||||
{"apac.anthropic.claude-sonnet-5-v1", BetaPolicyActionPass, "bedrock apac. sonnet-5"},
|
||||
{"jp.anthropic.claude-sonnet-5-v1", BetaPolicyActionPass, "bedrock jp. sonnet-5"},
|
||||
{"au.anthropic.claude-sonnet-5-v1", BetaPolicyActionPass, "bedrock au. sonnet-5"},
|
||||
{"us-gov.anthropic.claude-sonnet-5-v1", BetaPolicyActionPass, "bedrock us-gov. sonnet-5"},
|
||||
{"global.anthropic.claude-sonnet-5-v1", BetaPolicyActionPass, "bedrock global. sonnet-5"},
|
||||
{"anthropic.claude-sonnet-5-v1", BetaPolicyActionPass, "bedrock no-region sonnet-5"},
|
||||
|
||||
// —— sonnet-4.x 及以下必须过滤 ——
|
||||
{"claude-sonnet-4-6", BetaPolicyActionFilter, "sonnet-4.6 must be filtered"},
|
||||
{"claude-sonnet-4-5-20250929", BetaPolicyActionFilter, "sonnet-4.5 dated must be filtered"},
|
||||
{"claude-sonnet-4", BetaPolicyActionFilter, "sonnet-4 must be filtered"},
|
||||
{"claude-sonnet-4-5@20250929", BetaPolicyActionFilter, "sonnet-4.5 Vertex format must be filtered"},
|
||||
{"us.anthropic.claude-sonnet-4-6", BetaPolicyActionFilter, "bedrock us. sonnet-4.6 must be filtered"},
|
||||
{"us.anthropic.claude-sonnet-4-5-20250929-v1:0", BetaPolicyActionFilter, "bedrock us. sonnet-4.5 must be filtered"},
|
||||
// —— Opus / Haiku 必须过滤(无 1M) ——
|
||||
{"claude-opus-4-8", BetaPolicyActionFilter, "opus must be filtered"},
|
||||
{"claude-opus-4-7", BetaPolicyActionFilter, "opus 4.7 must be filtered"},
|
||||
{"us.anthropic.claude-opus-4-8-v1", BetaPolicyActionFilter, "bedrock opus 4.8 must be filtered"},
|
||||
{"claude-haiku-4-5", BetaPolicyActionFilter, "haiku must be filtered"},
|
||||
{"us.anthropic.claude-haiku-4-5-20251001-v1:0", BetaPolicyActionFilter, "bedrock haiku must be filtered"},
|
||||
{"claude-3-5-sonnet-20241022", BetaPolicyActionFilter, "legacy sonnet 3.5 must be filtered"},
|
||||
// —— 特殊边界:不应把 "claude-sonnet-50" / "claude-sonnet-5.1" 之类意外命名误放行 ——
|
||||
{"claude-sonnet-50", BetaPolicyActionFilter, "must not over-match a hypothetical sonnet-50"},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.model, func(t *testing.T) {
|
||||
action, _ := resolveRuleAction(*rule, tc.model)
|
||||
require.Equal(t, tc.wantAction, action, tc.desc)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -489,6 +489,23 @@ func DefaultRateLimit429CooldownSettings() *RateLimit429CooldownSettings {
|
||||
}
|
||||
|
||||
// DefaultBetaPolicySettings 返回默认的 Beta 策略配置
|
||||
//
|
||||
// context-1m-2025-08-07 的默认策略:
|
||||
// - 仅 claude-sonnet-5 及后续版本(如 claude-sonnet-5-*)在上游默认支持 1M 上下文。
|
||||
// - Sonnet 4.x 及以下、Opus、Haiku 上游都不支持该 beta,透传上去会被上游 400 或降级。
|
||||
// - 因此默认对 sonnet-5* 放行、其余全部过滤,与上游能力保持一致。
|
||||
//
|
||||
// 白名单需要覆盖每个上游路径的模型 ID 变形:
|
||||
// - 直连 Anthropic API(OAuth mimic / API Key / SetupToken):模型保持客户端原样
|
||||
// (如 "claude-sonnet-5"、"claude-sonnet-5-YYYYMMDD"、"claude-sonnet-5-thinking")。
|
||||
// - Vertex AI:normalizeVertexAnthropicModelID 会把 "-YYYYMMDD" 后缀转成 "@YYYYMMDD"
|
||||
// (如 "claude-sonnet-5@YYYYMMDD")。
|
||||
// - AWS Bedrock:ResolveBedrockModelID 会输出带跨区域前缀的模型 ID
|
||||
// (us./eu./apac./jp./au./us-gov./global. 或无前缀的 "anthropic." 形式)。
|
||||
//
|
||||
// 白名单只用后缀通配符(matchModelPattern 语义),因此每个路径都需要显式列出前缀。
|
||||
// 精确匹配 "claude-sonnet-5" + 后缀 "-*" 与 "@*",可覆盖直连/Vertex 场景,同时避免误伤
|
||||
// 未来可能出现的 "claude-sonnet-50" 或 "claude-sonnet-5.x" 之类的意外命名。
|
||||
func DefaultBetaPolicySettings() *BetaPolicySettings {
|
||||
return &BetaPolicySettings{
|
||||
Rules: []BetaPolicyRule{
|
||||
@@ -499,8 +516,26 @@ func DefaultBetaPolicySettings() *BetaPolicySettings {
|
||||
},
|
||||
{
|
||||
BetaToken: "context-1m-2025-08-07",
|
||||
Action: BetaPolicyActionFilter,
|
||||
Action: BetaPolicyActionPass,
|
||||
Scope: BetaPolicyScopeAll,
|
||||
ModelWhitelist: []string{
|
||||
// 直连 Anthropic API(客户端请求 model 原样)
|
||||
"claude-sonnet-5",
|
||||
"claude-sonnet-5-*",
|
||||
// Vertex AI 走 normalizeVertexAnthropicModelID 后 "@YYYYMMDD" 格式
|
||||
"claude-sonnet-5@*",
|
||||
// AWS Bedrock cross-region inference profile
|
||||
"us.anthropic.claude-sonnet-5*",
|
||||
"eu.anthropic.claude-sonnet-5*",
|
||||
"apac.anthropic.claude-sonnet-5*",
|
||||
"jp.anthropic.claude-sonnet-5*",
|
||||
"au.anthropic.claude-sonnet-5*",
|
||||
"us-gov.anthropic.claude-sonnet-5*",
|
||||
"global.anthropic.claude-sonnet-5*",
|
||||
// AWS Bedrock 无 cross-region 前缀
|
||||
"anthropic.claude-sonnet-5*",
|
||||
},
|
||||
FallbackAction: BetaPolicyActionFilter,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ export const claudeModels = [
|
||||
'claude-opus-4-7',
|
||||
'claude-opus-4-8',
|
||||
'claude-sonnet-4-6',
|
||||
'claude-sonnet-5',
|
||||
'claude-fable-5'
|
||||
]
|
||||
|
||||
@@ -243,6 +244,7 @@ export const allModels = allModelsList.map(m => ({ value: m, label: m }))
|
||||
|
||||
const anthropicPresetMappings = [
|
||||
{ label: 'Fable 5', from: 'claude-fable-5', to: 'claude-fable-5', color: 'bg-rose-100 text-rose-700 hover:bg-rose-200 dark:bg-rose-900/30 dark:text-rose-400' },
|
||||
{ label: 'Sonnet 5', from: 'claude-sonnet-5', to: 'claude-sonnet-5', color: 'bg-indigo-100 text-indigo-700 hover:bg-indigo-200 dark:bg-indigo-900/30 dark:text-indigo-400' },
|
||||
{ label: 'Sonnet 4', from: 'claude-sonnet-4-20250514', to: 'claude-sonnet-4-20250514', color: 'bg-blue-100 text-blue-700 hover:bg-blue-200 dark:bg-blue-900/30 dark:text-blue-400' },
|
||||
{ label: 'Sonnet 4.5', from: 'claude-sonnet-4-5-20250929', to: 'claude-sonnet-4-5-20250929', color: 'bg-indigo-100 text-indigo-700 hover:bg-indigo-200 dark:bg-indigo-900/30 dark:text-indigo-400' },
|
||||
{ label: 'Sonnet 4.6', from: 'claude-sonnet-4-6', to: 'claude-sonnet-4-6', color: 'bg-indigo-100 text-indigo-700 hover:bg-indigo-200 dark:bg-indigo-900/30 dark:text-indigo-400' },
|
||||
@@ -328,6 +330,7 @@ const bedrockPresetMappings = [
|
||||
{ label: 'Opus 4.6', from: 'claude-opus-4-6', to: 'us.anthropic.claude-opus-4-6-v1', color: 'bg-pink-100 text-pink-700 hover:bg-pink-200 dark:bg-pink-900/30 dark:text-pink-400' },
|
||||
{ label: 'Opus 4.7', from: 'claude-opus-4-7', to: 'us.anthropic.claude-opus-4-7-v1', color: 'bg-pink-100 text-pink-700 hover:bg-pink-200 dark:bg-pink-900/30 dark:text-pink-400' },
|
||||
{ label: 'Opus 4.8', from: 'claude-opus-4-8', to: 'us.anthropic.claude-opus-4-8-v1', color: 'bg-pink-100 text-pink-700 hover:bg-pink-200 dark:bg-pink-900/30 dark:text-pink-400' },
|
||||
{ label: 'Sonnet 5', from: 'claude-sonnet-5', to: 'us.anthropic.claude-sonnet-5-v1', color: 'bg-indigo-100 text-indigo-700 hover:bg-indigo-200 dark:bg-indigo-900/30 dark:text-indigo-400' },
|
||||
{ label: 'Sonnet 4.6', from: 'claude-sonnet-4-6', to: 'us.anthropic.claude-sonnet-4-6', color: 'bg-cyan-100 text-cyan-700 hover:bg-cyan-200 dark:bg-cyan-900/30 dark:text-cyan-400' },
|
||||
{ label: 'Opus 4.5', from: 'claude-opus-4-5-thinking', to: 'us.anthropic.claude-opus-4-5-20251101-v1:0', color: 'bg-pink-100 text-pink-700 hover:bg-pink-200 dark:bg-pink-900/30 dark:text-pink-400' },
|
||||
{ label: 'Sonnet 4.5', from: 'claude-sonnet-4-5', to: 'us.anthropic.claude-sonnet-4-5-20250929-v1:0', color: 'bg-cyan-100 text-cyan-700 hover:bg-cyan-200 dark:bg-cyan-900/30 dark:text-cyan-400' },
|
||||
|
||||
Reference in New Issue
Block a user