Просмотр исходного кода

feat: support claude beta (#417)

* feat: support claude beta

* fix: ci lint
zijiren 1 месяц назад
Родитель
Сommit
e3a95d5a5b

+ 19 - 12
core/relay/adaptor/anthropic/adaptor.go

@@ -48,6 +48,7 @@ const (
 	AnthropicVersion = "2023-06-01"
 	//nolint:gosec
 	AnthropicTokenHeader = "X-Api-Key"
+	AnthropicBeta        = "Anthropic-Beta"
 )
 
 func (a *Adaptor) SetupRequestHeader(
@@ -65,21 +66,27 @@ func (a *Adaptor) SetupRequestHeader(
 
 	req.Header.Set("Anthropic-Version", anthropicVersion)
 
-	// https://docs.anthropic.com/en/api/beta-headers
-	req.Header.Set("Anthropic-Beta", "messages-2023-12-15")
+	rawBetas := c.Request.Header.Get(AnthropicBeta)
 
-	// https://x.com/alexalbert__/status/1812921642143900036
-	// claude-3-5-sonnet can support 8k context
-	if strings.HasPrefix(meta.ActualModel, "claude-3-5-sonnet") {
-		req.Header.Set("Anthropic-Beta", "max-tokens-3-5-sonnet-2024-07-15")
-	}
+	if rawBetas != "" {
+		req.Header.Set(AnthropicBeta, rawBetas)
+	} else {
+		// https://docs.anthropic.com/en/api/beta-headers
+		req.Header.Set(AnthropicBeta, "messages-2023-12-15")
 
-	if strings.HasPrefix(meta.ActualModel, "claude-3-7-sonnet") {
-		req.Header.Set("Anthropic-Beta", "output-128k-2025-02-19")
-	}
+		// https://x.com/alexalbert__/status/1812921642143900036
+		// claude-3-5-sonnet can support 8k context
+		if strings.HasPrefix(meta.ActualModel, "claude-3-5-sonnet") {
+			req.Header.Set(AnthropicBeta, "max-tokens-3-5-sonnet-2024-07-15")
+		}
 
-	// https://docs.anthropic.com/en/docs/build-with-claude/prompt-caching#1-hour-cache-duration-beta
-	// req.Header.Set("Anthropic-Beta", "extended-cache-ttl-2025-04-11")
+		if strings.HasPrefix(meta.ActualModel, "claude-3-7-sonnet") {
+			req.Header.Set(AnthropicBeta, "output-128k-2025-02-19")
+		}
+
+		// https://docs.anthropic.com/en/docs/build-with-claude/prompt-caching#1-hour-cache-duration-beta
+		// req.Header.Set(AnthropicBeta, "extended-cache-ttl-2025-04-11")
+	}
 
 	return nil
 }

+ 38 - 1
core/relay/adaptor/aws/claude/adapter.go

@@ -3,6 +3,7 @@ package aws
 import (
 	"fmt"
 	"net/http"
+	"strings"
 
 	"github.com/aws/aws-sdk-go-v2/aws"
 	"github.com/aws/aws-sdk-go-v2/service/bedrockruntime"
@@ -27,7 +28,8 @@ const (
 type Adaptor struct{}
 
 type Request struct {
-	AnthropicVersion string `json:"anthropic_version"`
+	AnthropicBeta    []string `json:"anthropic_beta,omitempty"`
+	AnthropicVersion string   `json:"anthropic_version"`
 	*relaymodel.ClaudeRequest
 }
 
@@ -62,6 +64,10 @@ func (a *Adaptor) ConvertRequest(
 	}, nil
 }
 
+var supportedContextManagementEditsType = map[string]struct{}{
+	"clear_tool_uses_20250919": {},
+}
+
 func handleChatCompletionsRequest(meta *meta.Meta, request *http.Request) ([]byte, error) {
 	claudeReq, err := anthropic.OpenAIConvertRequest(meta, request)
 	if err != nil {
@@ -78,6 +84,10 @@ func handleChatCompletionsRequest(meta *meta.Meta, request *http.Request) ([]byt
 		ClaudeRequest:    claudeReq,
 	}
 
+	if betas := request.Header.Get(anthropic.AnthropicBeta); betas != "" {
+		req.AnthropicBeta = strings.Split(betas, ",")
+	}
+
 	return sonic.Marshal(req)
 }
 
@@ -87,6 +97,33 @@ func handleAnthropicRequest(meta *meta.Meta, request *http.Request) ([]byte, err
 			return err
 		}
 
+		if betas := request.Header.Get(anthropic.AnthropicBeta); betas != "" {
+			node.SetAny("anthropic_beta", strings.Split(betas, ","))
+		}
+
+		editesNode := node.GetByPath("context_management", "edits")
+		if editesNode.Check() == nil {
+			nodeLen, _ := editesNode.Len()
+			newEdits := make([]ast.Node, 0, nodeLen)
+			_ = editesNode.
+				ForEach(func(path ast.Sequence, node *ast.Node) bool {
+					t, err := node.Get("type").String()
+					if err != nil {
+						return true
+					}
+
+					if _, ok := supportedContextManagementEditsType[t]; !ok {
+						return true
+					}
+
+					newEdits = append(newEdits, *node)
+
+					return true
+				})
+
+			*editesNode = ast.NewArray(newEdits)
+		}
+
 		stream, _ := node.Get("stream").Bool()
 		meta.Set("stream", stream)
 

+ 7 - 1
core/relay/adaptor/vertexai/adaptor.go

@@ -10,6 +10,7 @@ import (
 	"github.com/gin-gonic/gin"
 	"github.com/labring/aiproxy/core/model"
 	"github.com/labring/aiproxy/core/relay/adaptor"
+	"github.com/labring/aiproxy/core/relay/adaptor/anthropic"
 	"github.com/labring/aiproxy/core/relay/meta"
 	"github.com/labring/aiproxy/core/relay/mode"
 	relaymodel "github.com/labring/aiproxy/core/relay/model"
@@ -156,9 +157,14 @@ func (a *Adaptor) GetRequestURL(meta *meta.Meta, _ adaptor.Store) (adaptor.Reque
 func (a *Adaptor) SetupRequestHeader(
 	meta *meta.Meta,
 	_ adaptor.Store,
-	_ *gin.Context,
+	c *gin.Context,
 	req *http.Request,
 ) error {
+	betas := c.Request.Header.Get(anthropic.AnthropicBeta)
+	if betas != "" {
+		req.Header.Set(anthropic.AnthropicBeta, betas)
+	}
+
 	config, err := getConfigFromKey(meta.Channel.Key)
 	if err != nil {
 		return err