package aiwire import ( "encoding/json" "strings" ) // MessagesRequest 是 Anthropic /v1/messages 请求体。 type MessagesRequest struct { Model string `json:"model"` MaxTokens int `json:"max_tokens"` System json.RawMessage `json:"system,omitempty"` Messages []AnthMessage `json:"messages"` StopSequences []string `json:"stop_sequences,omitempty"` Temperature *float64 `json:"temperature,omitempty"` TopP *float64 `json:"top_p,omitempty"` TopK *int `json:"top_k,omitempty"` Stream bool `json:"stream,omitempty"` Tools []AnthTool `json:"tools,omitempty"` ToolChoice json.RawMessage `json:"tool_choice,omitempty"` Metadata json.RawMessage `json:"metadata,omitempty"` Thinking json.RawMessage `json:"thinking,omitempty"` } // SystemText 把顶层 system(string 或块数组)拍平为文本。 func (r MessagesRequest) SystemText() string { if len(r.System) == 0 { return "" } var s string if err := json.Unmarshal(r.System, &s); err == nil { return s } var blocks []AnthBlock if err := json.Unmarshal(r.System, &blocks); err != nil { return "" } var sb strings.Builder for _, b := range blocks { if b.Type == "text" { sb.WriteString(b.Text) } } return sb.String() } // AnthMessage 是 Anthropic 消息;Content 兼容 string 与块数组。 type AnthMessage struct { Role string `json:"role"` Content AnthContent `json:"content"` } // AnthContent 是 string | []AnthBlock 联合。 type AnthContent struct { Text string Blocks []AnthBlock isArray bool } func (c *AnthContent) UnmarshalJSON(b []byte) error { trimmed := strings.TrimSpace(string(b)) if trimmed == "null" { return nil } if len(trimmed) > 0 && trimmed[0] == '[' { c.isArray = true return json.Unmarshal(b, &c.Blocks) } return json.Unmarshal(b, &c.Text) } func (c AnthContent) MarshalJSON() ([]byte, error) { if c.isArray { return json.Marshal(c.Blocks) } return json.Marshal(c.Text) } // AllBlocks 统一为块数组视图(字符串形态包装为单 text 块)。 func (c AnthContent) AllBlocks() []AnthBlock { if c.isArray { return c.Blocks } if c.Text == "" { return nil } return []AnthBlock{{Type: "text", Text: c.Text}} } // AnthBlock 是内容块;按 Type 取字段(text / tool_use / tool_result / image / thinking)。 type AnthBlock struct { Type string `json:"type"` Text string `json:"text,omitempty"` // tool_use ID string `json:"id,omitempty"` Name string `json:"name,omitempty"` Input json.RawMessage `json:"input,omitempty"` // tool_result ToolUseID string `json:"tool_use_id,omitempty"` Content json.RawMessage `json:"content,omitempty"` IsError bool `json:"is_error,omitempty"` // image Source json.RawMessage `json:"source,omitempty"` } // ResultText 把 tool_result 的 content(string 或块数组)拍平为文本。 func (b AnthBlock) ResultText() string { if len(b.Content) == 0 { return "" } var s string if err := json.Unmarshal(b.Content, &s); err == nil { return s } var blocks []AnthBlock if err := json.Unmarshal(b.Content, &blocks); err != nil { return string(b.Content) } var sb strings.Builder for _, blk := range blocks { if blk.Type == "text" { sb.WriteString(blk.Text) } } return sb.String() } // AnthTool 是 Anthropic 工具定义(input_schema 即 JSON Schema)。 type AnthTool struct { Name string `json:"name"` Description string `json:"description,omitempty"` InputSchema json.RawMessage `json:"input_schema,omitempty"` } // MessagesResponse 是非流式响应体。 type MessagesResponse struct { ID string `json:"id"` Type string `json:"type"` Role string `json:"role"` Model string `json:"model"` Content []AnthBlock `json:"content"` StopReason string `json:"stop_reason"` StopSequence *string `json:"stop_sequence"` Usage AnthUsage `json:"usage"` } // AnthUsage 是 Anthropic 用量结构。 type AnthUsage struct { InputTokens int `json:"input_tokens"` OutputTokens int `json:"output_tokens"` // CacheReadInputTokens 是缓存命中读取数;OCI 隐式缓存无「写入」计数,cache_creation 恒缺 CacheReadInputTokens int `json:"cache_read_input_tokens,omitempty"` } // AnthErrorBody 是 Anthropic 格式错误响应。 type AnthErrorBody struct { Type string `json:"type"` Error AnthErrorDetail `json:"error"` } // AnthErrorDetail 是错误明细。 type AnthErrorDetail struct { Type string `json:"type"` Message string `json:"message"` }