diff --git a/backend/internal/pkg/apicompat/types.go b/backend/internal/pkg/apicompat/types.go index cfc4b0aa..f8c6b75f 100644 --- a/backend/internal/pkg/apicompat/types.go +++ b/backend/internal/pkg/apicompat/types.go @@ -82,10 +82,18 @@ type AnthropicImageSource struct { // AnthropicTool describes a tool available to the model. type AnthropicTool struct { - Type string `json:"type,omitempty"` // e.g. "web_search_20250305" for server tools - Name string `json:"name"` - Description string `json:"description,omitempty"` - InputSchema json.RawMessage `json:"input_schema"` // JSON Schema object + Type string `json:"type,omitempty"` // e.g. "web_search_20250305" for server tools + Name string `json:"name"` + Description string `json:"description,omitempty"` + InputSchema json.RawMessage `json:"input_schema"` // JSON Schema object + CacheControl *AnthropicCacheControl `json:"cache_control,omitempty"` +} + +// AnthropicCacheControl 对应 Anthropic API 的 cache_control 字段。 +// ttl 默认由调用方决定;本项目策略见 claude.DefaultCacheControlTTL。 +type AnthropicCacheControl struct { + Type string `json:"type"` // "ephemeral" + TTL string `json:"ttl,omitempty"` // "5m" / "1h" / 省略=默认 5m(由 Anthropic 判定) } // AnthropicResponse is the non-streaming response from POST /v1/messages. diff --git a/backend/internal/pkg/claude/constants.go b/backend/internal/pkg/claude/constants.go index 3c92c3e3..594d91ad 100644 --- a/backend/internal/pkg/claude/constants.go +++ b/backend/internal/pkg/claude/constants.go @@ -57,6 +57,11 @@ const APIKeyBetaHeader = BetaClaudeCode + "," + BetaInterleavedThinking + "," + // APIKeyHaikuBetaHeader Haiku 模型在 API-key 账号下使用的 anthropic-beta header(不包含 oauth / claude-code) const APIKeyHaikuBetaHeader = BetaInterleavedThinking +// DefaultCacheControlTTL 是网关代理为自己生成的 cache_control 块默认使用的 ttl。 +// 真实 Claude Code CLI 当前使用 "1h",但本仓策略是"客户端透传 ttl 优先; +// 客户端缺省时统一使用 5m",这样既不浪费 1h 缓存额度,也保留客户端自定义能力。 +const DefaultCacheControlTTL = "5m" + // FullClaudeCodeMimicryBetas 返回最"像"真实 Claude Code CLI 的完整 beta 列表, // 用于 OAuth 账号伪装成 Claude Code 时使用。 // 顺序与真实 CLI 抓包一致。 diff --git a/backend/internal/service/gateway_service.go b/backend/internal/service/gateway_service.go index 4315808c..5ca5de0c 100644 --- a/backend/internal/service/gateway_service.go +++ b/backend/internal/service/gateway_service.go @@ -850,6 +850,7 @@ func (s *GatewayService) hashContent(content string) string { type anthropicCacheControlPayload struct { Type string `json:"type"` + TTL string `json:"ttl,omitempty"` } type anthropicSystemTextBlockPayload struct { @@ -898,7 +899,10 @@ func marshalAnthropicSystemTextBlock(text string, includeCacheControl bool) ([]b Text: text, } if includeCacheControl { - block.CacheControl = &anthropicCacheControlPayload{Type: "ephemeral"} + block.CacheControl = &anthropicCacheControlPayload{ + Type: "ephemeral", + TTL: claude.DefaultCacheControlTTL, + } } return json.Marshal(block) } @@ -3856,7 +3860,7 @@ func rewriteSystemForNonClaudeCode(body []byte, system any) []byte { { "type": "text", "text": claudeCodeSystemPrompt, - "cache_control": map[string]string{"type": "ephemeral"}, + "cache_control": map[string]string{"type": "ephemeral", "ttl": claude.DefaultCacheControlTTL}, }, } out, ok := setJSONValueBytes(body, "system", claudeCodeSystemBlock)