feat: publish sanitized custom.97 source
This commit is contained in:
1022 files changed
+69903
-19207
No files matched your search
+80
-15
@@ -3,6 +3,7 @@ package dto
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"maps"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@@ -135,6 +136,9 @@ type ClaudeMessageSource struct {
|
||||
type ClaudeMessage struct {
|
||||
Role string `json:"role"`
|
||||
Content any `json:"content"`
|
||||
// OutputConfig carries per-message effort (beta): an effort-only system
|
||||
// message has empty content and the new level in output_config.effort.
|
||||
OutputConfig json.RawMessage `json:"output_config,omitempty"`
|
||||
}
|
||||
|
||||
func (c *ClaudeMessage) IsStringContent() bool {
|
||||
@@ -238,6 +242,7 @@ type ClaudeRequest struct {
|
||||
TopK *int `json:"top_k,omitempty"`
|
||||
Stream *bool `json:"stream,omitempty"`
|
||||
Tools any `json:"tools,omitempty"`
|
||||
Safeguards json.RawMessage `json:"safeguards,omitempty"`
|
||||
ContextManagement json.RawMessage `json:"context_management,omitempty"`
|
||||
OutputConfig json.RawMessage `json:"output_config,omitempty"`
|
||||
OutputFormat json.RawMessage `json:"output_format,omitempty"`
|
||||
@@ -332,9 +337,25 @@ func (c *ClaudeRequest) GetTokenCountMeta() *types.TokenCountMeta {
|
||||
texts = append(texts, string(b))
|
||||
}
|
||||
case "tool_result":
|
||||
if media.Content != nil {
|
||||
b, _ := kitutil.Marshal(media.Content)
|
||||
texts = append(texts, string(b))
|
||||
// Count tool output as the text the model reads; JSON-encoding it
|
||||
// would escape newlines and quotes and count image base64 as text.
|
||||
if media.IsStringContent() {
|
||||
texts = append(texts, media.GetStringContent())
|
||||
break
|
||||
}
|
||||
for _, part := range media.ParseMediaContent() {
|
||||
if source := part.ToFileSource(); source != nil {
|
||||
fileType := types.FileTypeFile
|
||||
if part.Type == "image" {
|
||||
fileType = types.FileTypeImage
|
||||
}
|
||||
fileMeta = append(fileMeta, &types.FileMeta{FileType: fileType, Source: source})
|
||||
} else if part.Type == "text" {
|
||||
texts = append(texts, part.GetText())
|
||||
} else {
|
||||
b, _ := kitutil.Marshal(part)
|
||||
texts = append(texts, string(b))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -391,18 +412,6 @@ func (c *ClaudeRequest) SetModelName(modelName string) {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ClaudeRequest) SearchToolNameByToolCallId(toolCallId string) string {
|
||||
for _, message := range c.Messages {
|
||||
content, _ := message.ParseContent()
|
||||
for _, mediaMessage := range content {
|
||||
if mediaMessage.Id == toolCallId {
|
||||
return mediaMessage.Name
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// AddTool 添加工具到请求中
|
||||
func (c *ClaudeRequest) AddTool(tool any) {
|
||||
if c.Tools == nil {
|
||||
@@ -456,6 +465,18 @@ func ProcessTools(tools []any) ([]*Tool, []*ClaudeWebSearchTool) {
|
||||
normalTools = append(normalTools, &t)
|
||||
case ClaudeWebSearchTool:
|
||||
webSearchTools = append(webSearchTools, &t)
|
||||
case map[string]any:
|
||||
// Tools decoded from client JSON arrive as maps.
|
||||
toolType, _ := t["type"].(string)
|
||||
if strings.HasPrefix(toolType, "web_search") {
|
||||
if webSearchTool, err := kitutil.Any2Type[ClaudeWebSearchTool](t); err == nil {
|
||||
webSearchTools = append(webSearchTools, &webSearchTool)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if normalTool, err := kitutil.Any2Type[Tool](t); err == nil {
|
||||
normalTools = append(normalTools, &normalTool)
|
||||
}
|
||||
default:
|
||||
// 未知类型,跳过
|
||||
continue
|
||||
@@ -473,6 +494,50 @@ type Thinking struct {
|
||||
// the visible summary that was default on Opus 4.6; "omitted" (default on
|
||||
// 4.7) suppresses it. Pass-through field from upstream Anthropic API.
|
||||
Display string `json:"display,omitempty"`
|
||||
// BlockBinding carries the client's preserved-thinking controls, such as
|
||||
// {"prefix_mismatch_behavior":"drop_block"} (beta
|
||||
// thinking-binding-controls-2026-08-01). It is forwarded verbatim and
|
||||
// never added by the gateway.
|
||||
BlockBinding json.RawMessage `json:"block_binding,omitempty"`
|
||||
|
||||
// Extra keeps thinking fields this struct does not model yet, so a native
|
||||
// Claude request forwards them instead of silently dropping them. It is
|
||||
// exported so common.DeepCopy copies it; MarshalJSON writes it back.
|
||||
Extra map[string]json.RawMessage `json:"-"`
|
||||
}
|
||||
|
||||
func (c *Thinking) UnmarshalJSON(data []byte) error {
|
||||
type thinkingFields Thinking
|
||||
var fields thinkingFields
|
||||
if err := kitutil.Unmarshal(data, &fields); err != nil {
|
||||
return err
|
||||
}
|
||||
var all map[string]json.RawMessage
|
||||
if err := kitutil.Unmarshal(data, &all); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, known := range []string{"type", "budget_tokens", "display", "block_binding"} {
|
||||
delete(all, known)
|
||||
}
|
||||
*c = Thinking(fields)
|
||||
if len(all) > 0 {
|
||||
c.Extra = all
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c Thinking) MarshalJSON() ([]byte, error) {
|
||||
type thinkingFields Thinking
|
||||
data, err := kitutil.Marshal(thinkingFields(c))
|
||||
if err != nil || len(c.Extra) == 0 {
|
||||
return data, err
|
||||
}
|
||||
merged := make(map[string]json.RawMessage, len(c.Extra)+4)
|
||||
maps.Copy(merged, c.Extra)
|
||||
if err := kitutil.Unmarshal(data, &merged); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return kitutil.Marshal(merged)
|
||||
}
|
||||
|
||||
func (c *Thinking) GetBudgetTokens() int {
|
||||
|
||||
Reference in new issue
Block a user