// Package aiwire 定义 AI 网关的线格式类型:OpenAI Responses / Chat Completions / // Anthropic Messages / Embeddings 与通用记账、错误、模型列表结构。上游统一为 // OCI OpenAI 兼容面。 package aiwire import ( "encoding/json" "strings" ) // StringList 兼容 OpenAI stop 的 string 与 []string 两种线格式。 type StringList []string func (s *StringList) UnmarshalJSON(b []byte) error { if len(b) > 0 && b[0] == '"' { var one string if err := json.Unmarshal(b, &one); err != nil { return err } *s = StringList{one} return nil } var many []string if err := json.Unmarshal(b, &many); err != nil { return err } *s = many return nil } // Usage 是 token 用量。 type Usage struct { PromptTokens int `json:"prompt_tokens"` CompletionTokens int `json:"completion_tokens"` TotalTokens int `json:"total_tokens"` // PromptTokensDetails 携带缓存命中细分;OCI 为隐式 prompt 缓存,只有读取计数 PromptTokensDetails *PromptTokensDetails `json:"prompt_tokens_details,omitempty"` } // PromptTokensDetails 是输入 token 细分(OpenAI prompt_tokens_details 同构)。 type PromptTokensDetails struct { CachedTokens int `json:"cached_tokens"` } // CachedTokens 返回缓存命中读取的 token 数;无细分时为 0。 func (u *Usage) CachedTokens() int { if u == nil || u.PromptTokensDetails == nil { return 0 } return u.PromptTokensDetails.CachedTokens } // Model 是 /v1/models 列表项。 type Model struct { ID string `json:"id"` Object string `json:"object"` Created int64 `json:"created"` OwnedBy string `json:"owned_by"` } // ModelList 是 /v1/models 响应体。 type ModelList struct { Object string `json:"object"` Data []Model `json:"data"` } // ErrorBody 是 OpenAI 格式错误响应。 type ErrorBody struct { Error ErrorDetail `json:"error"` } // ErrorDetail 是错误明细。 type ErrorDetail struct { Message string `json:"message"` Type string `json:"type"` Code string `json:"code,omitempty"` } // ---- Chat Completions 线格式(/ai/v1/chat/completions,Tier 2 兼容层)---- // ChatRequest 是 OpenAI /v1/chat/completions 请求体;只声明网关读取的字段, // 无对应物的字段(stop / seed / n / penalty 等)由 JSON 解码天然忽略。 type ChatRequest struct { Model string `json:"model"` Messages []ChatMessage `json:"messages"` MaxTokens *int `json:"max_tokens,omitempty"` MaxCompletionTokens *int `json:"max_completion_tokens,omitempty"` Temperature *float64 `json:"temperature,omitempty"` TopP *float64 `json:"top_p,omitempty"` ParallelToolCalls *bool `json:"parallel_tool_calls,omitempty"` Stream bool `json:"stream,omitempty"` StreamOptions *StreamOptions `json:"stream_options,omitempty"` Tools []Tool `json:"tools,omitempty"` ToolChoice json.RawMessage `json:"tool_choice,omitempty"` ResponseFormat *ResponseFormat `json:"response_format,omitempty"` ReasoningEffort string `json:"reasoning_effort,omitempty"` } // StreamOptions 控制流式末尾是否附带 usage 块。 type StreamOptions struct { IncludeUsage bool `json:"include_usage"` } // ChatMessage 是请求与非流式响应共用的消息结构。 type ChatMessage struct { Role string `json:"role"` Content Content `json:"content"` ToolCalls []ToolCall `json:"tool_calls,omitempty"` ToolCallID string `json:"tool_call_id,omitempty"` } // Content 兼容 string 与多模态块数组两种线格式,序列化时保形。 type Content struct { Text string Parts []ContentPart IsArray bool } func (c *Content) UnmarshalJSON(b []byte) error { t := strings.TrimSpace(string(b)) if t == "null" { return nil } if strings.HasPrefix(t, "[") { c.IsArray = true return json.Unmarshal(b, &c.Parts) } return json.Unmarshal(b, &c.Text) } func (c Content) MarshalJSON() ([]byte, error) { if c.IsArray { return json.Marshal(c.Parts) } return json.Marshal(c.Text) } // NewTextContent 构造字符串形态的内容。 func NewTextContent(text string) Content { return Content{Text: text} } // JoinText 拍平内容为纯文本(数组形态拼接 text 块)。 func (c Content) JoinText() string { if !c.IsArray { return c.Text } var sb strings.Builder for _, p := range c.Parts { if p.Type == "" || p.Type == "text" { sb.WriteString(p.Text) } } return sb.String() } // ContentPart 是块数组中的一段;text 与 image_url 被网关承接,其余类型拒绝。 type ContentPart struct { Type string `json:"type"` Text string `json:"text,omitempty"` ImageURL *ImageURL `json:"image_url,omitempty"` } // ImageURL 是 image_url 块负载;url 为 data URI(base64)或公网地址。 type ImageURL struct { URL string `json:"url"` Detail string `json:"detail,omitempty"` } // Tool 是嵌套形态的 function 工具定义。 type Tool struct { Type string `json:"type"` Function FunctionDef `json:"function"` } // FunctionDef 描述工具的名称与 JSON Schema 参数。 type FunctionDef struct { Name string `json:"name"` Description string `json:"description,omitempty"` Parameters json.RawMessage `json:"parameters,omitempty"` } // ToolCall 是助手消息中的工具调用。 type ToolCall struct { ID string `json:"id"` Type string `json:"type"` Function FunctionCall `json:"function"` } // FunctionCall 携带调用名与 JSON 编码的实参。 type FunctionCall struct { Name string `json:"name"` Arguments string `json:"arguments"` } // ResponseFormat 对应 response_format(json_object / json_schema)。 type ResponseFormat struct { Type string `json:"type"` JSONSchema json.RawMessage `json:"json_schema,omitempty"` } // ChatResponse 是非流式响应体(chat.completion)。 type ChatResponse struct { ID string `json:"id"` Object string `json:"object"` Created int64 `json:"created"` Model string `json:"model"` Choices []Choice `json:"choices"` Usage *Usage `json:"usage,omitempty"` } // Choice 是一条候选回复。 type Choice struct { Index int `json:"index"` Message ChatMessage `json:"message"` FinishReason string `json:"finish_reason"` } // ChatChunk 是流式增量事件体(chat.completion.chunk)。 type ChatChunk struct { ID string `json:"id"` Object string `json:"object"` Created int64 `json:"created"` Model string `json:"model"` Choices []ChunkChoice `json:"choices"` Usage *Usage `json:"usage,omitempty"` } // ChunkChoice 是流式事件中的增量候选。 type ChunkChoice struct { Index int `json:"index"` Delta Delta `json:"delta"` FinishReason *string `json:"finish_reason"` } // Delta 是流式增量内容;Content 为纯文本片段。 type Delta struct { Role string `json:"role,omitempty"` Content string `json:"content,omitempty"` ToolCalls []ToolCallDelta `json:"tool_calls,omitempty"` } // ToolCallDelta 是工具调用的流式片段;Index 标识聚合目标。 type ToolCallDelta struct { Index int `json:"index"` ID string `json:"id,omitempty"` Type string `json:"type,omitempty"` Function FunctionCallDelta `json:"function"` } // FunctionCallDelta 是函数名 / 实参的增量片段。 type FunctionCallDelta struct { Name string `json:"name,omitempty"` Arguments string `json:"arguments,omitempty"` }