166 lines
4.6 KiB
Go
166 lines
4.6 KiB
Go
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"`
|
|
}
|