diff --git a/plugins/wasm-go/extensions/ai-proxy/provider/claude.go b/plugins/wasm-go/extensions/ai-proxy/provider/claude.go index 6db9b5d48..812ed43af 100644 --- a/plugins/wasm-go/extensions/ai-proxy/provider/claude.go +++ b/plugins/wasm-go/extensions/ai-proxy/provider/claude.go @@ -19,6 +19,10 @@ const ( claudeDomain = "api.anthropic.com" claudeDefaultVersion = "2023-06-01" claudeDefaultMaxTokens = 4096 + + // Claude Code mode constants + claudeCodeUserAgent = "claude-cli/2.1.2 (external, cli)" + claudeCodeBetaFeatures = "oauth-2025-04-20,interleaved-thinking-2025-05-14,claude-code-20250219" ) type claudeProviderInitializer struct{} @@ -319,13 +323,36 @@ func (c *claudeProvider) TransformRequestHeaders(ctx wrapper.HttpContext, apiNam util.OverwriteRequestPathHeaderByCapability(headers, string(apiName), c.config.capabilities) util.OverwriteRequestHostHeader(headers, claudeDomain) - headers.Set("x-api-key", c.config.GetApiTokenInUse(ctx)) - if c.config.apiVersion == "" { c.config.apiVersion = claudeDefaultVersion } - headers.Set("anthropic-version", c.config.apiVersion) + + // Check if Claude Code mode is enabled + if c.config.claudeCodeMode { + // Claude Code mode: use OAuth token with Bearer authorization + token := c.config.GetApiTokenInUse(ctx) + headers.Set("authorization", "Bearer "+token) + headers.Del("x-api-key") + + // Set Claude Code specific headers + headers.Set("user-agent", claudeCodeUserAgent) + headers.Set("x-app", "cli") + headers.Set("anthropic-beta", claudeCodeBetaFeatures) + + // Add ?beta=true query parameter to the path + currentPath := headers.Get(":path") + if currentPath != "" && !strings.Contains(currentPath, "beta=true") { + if strings.Contains(currentPath, "?") { + headers.Set(":path", currentPath+"&beta=true") + } else { + headers.Set(":path", currentPath+"?beta=true") + } + } + } else { + // Standard mode: use x-api-key + headers.Set("x-api-key", c.config.GetApiTokenInUse(ctx)) + } } func (c *claudeProvider) OnRequestBody(ctx wrapper.HttpContext, apiName ApiName, body []byte) (types.Action, error) { @@ -415,9 +442,25 @@ func (c *claudeProvider) buildClaudeTextGenRequest(origRequest *chatCompletionRe for _, message := range origRequest.Messages { if message.Role == roleSystem { - claudeRequest.System = &claudeSystemPrompt{ - StringValue: message.StringContent(), - IsArray: false, + // In Claude Code mode, use array format with cache_control + if c.config.claudeCodeMode { + claudeRequest.System = &claudeSystemPrompt{ + ArrayValue: []claudeChatMessageContent{ + { + Type: contentTypeText, + Text: message.StringContent(), + CacheControl: map[string]interface{}{ + "type": "ephemeral", + }, + }, + }, + IsArray: true, + } + } else { + claudeRequest.System = &claudeSystemPrompt{ + StringValue: message.StringContent(), + IsArray: false, + } } continue } diff --git a/plugins/wasm-go/extensions/ai-proxy/provider/provider.go b/plugins/wasm-go/extensions/ai-proxy/provider/provider.go index f7cebccf7..b46c7ed09 100644 --- a/plugins/wasm-go/extensions/ai-proxy/provider/provider.go +++ b/plugins/wasm-go/extensions/ai-proxy/provider/provider.go @@ -442,6 +442,9 @@ type ProviderConfig struct { // @Title zh-CN 豆包服务域名 // @Description zh-CN 仅适用于豆包服务,默认转发域名为 ark.cn-beijing.volces.com doubaoDomain string `required:"false" yaml:"doubaoDomain" json:"doubaoDomain"` + // @Title zh-CN Claude Code 模式 + // @Description zh-CN 仅适用于Claude服务。启用后将伪装成Claude Code客户端发起请求,支持使用Claude Code的OAuth Token进行认证。 + claudeCodeMode bool `required:"false" yaml:"claudeCodeMode" json:"claudeCodeMode"` } func (c *ProviderConfig) GetId() string { @@ -646,6 +649,7 @@ func (c *ProviderConfig) FromJson(json gjson.Result) { c.vllmServerHost = json.Get("vllmServerHost").String() c.vllmCustomUrl = json.Get("vllmCustomUrl").String() c.doubaoDomain = json.Get("doubaoDomain").String() + c.claudeCodeMode = json.Get("claudeCodeMode").Bool() c.contextCleanupCommands = make([]string, 0) for _, cmd := range json.Get("contextCleanupCommands").Array() { if cmd.String() != "" {