初始提交:OCI 面板后端(含 GenAI 网关一期)
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
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"`
|
||||
}
|
||||
Reference in New Issue
Block a user