- 新端点 /ai/v1/audio/speech(xai.grok-tts)、/rerank(cohere.rerank-v4)、/moderations(OCI Guardrails) - Responses 放行 code_interpreter 与远程 mcp 工具,web_search/x_search 解除仅非流式限制 - 模型能力映射扩展:TEXT_RERANK→RERANK、TEXT_TO_AUDIO→TTS - AI 网关文档独立 docs/ai-gateway.md,字段兼容矩阵只列支持项;README 精简引用 - swagger 修缺:135 处响应注解具体化,RawMessage/联合类型统一渲染 AnyJSON,overrides 迁至 docs/.swaggo - CHANGELOG 0.4.0,版本段不再记日期;DASH_VERSION v0.4.0
200 lines
6.4 KiB
Go
200 lines
6.4 KiB
Go
package aiwire
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strings"
|
|
)
|
|
|
|
// RespRequest 是 POST /responses 请求体(无状态子集;有状态字段仅用于拒绝判定)。
|
|
type RespRequest struct {
|
|
Model string `json:"model"`
|
|
Input RespInput `json:"input"`
|
|
Instructions string `json:"instructions,omitempty"`
|
|
MaxOutputTokens *int `json:"max_output_tokens,omitempty"`
|
|
Temperature *float64 `json:"temperature,omitempty"`
|
|
TopP *float64 `json:"top_p,omitempty"`
|
|
Tools []RespTool `json:"tools,omitempty"`
|
|
ToolChoice json.RawMessage `json:"tool_choice,omitempty"`
|
|
ParallelToolCalls *bool `json:"parallel_tool_calls,omitempty"`
|
|
Text *RespText `json:"text,omitempty"`
|
|
Reasoning *RespReasoning `json:"reasoning,omitempty"`
|
|
Stream bool `json:"stream,omitempty"`
|
|
Store *bool `json:"store,omitempty"`
|
|
PreviousResponseID string `json:"previous_response_id,omitempty"`
|
|
Conversation json.RawMessage `json:"conversation,omitempty"`
|
|
Background *bool `json:"background,omitempty"`
|
|
}
|
|
|
|
// RespInput 兼容 string 与 item 数组两种形态。
|
|
type RespInput struct {
|
|
Text string
|
|
Items []RespItem
|
|
IsArray bool
|
|
}
|
|
|
|
func (in *RespInput) UnmarshalJSON(b []byte) error {
|
|
t := strings.TrimSpace(string(b))
|
|
if strings.HasPrefix(t, "\"") {
|
|
return json.Unmarshal(b, &in.Text)
|
|
}
|
|
in.IsArray = true
|
|
return json.Unmarshal(b, &in.Items)
|
|
}
|
|
|
|
// RespItem 承载 input 数组的各形态:message / function_call / function_call_output。
|
|
type RespItem struct {
|
|
Type string `json:"type,omitempty"` // 缺省视为 message
|
|
Role string `json:"role,omitempty"`
|
|
Content RespContent `json:"content,omitempty"`
|
|
ID string `json:"id,omitempty"`
|
|
CallID string `json:"call_id,omitempty"`
|
|
Name string `json:"name,omitempty"`
|
|
Arguments string `json:"arguments,omitempty"`
|
|
Output json.RawMessage `json:"output,omitempty"` // string 或部件数组
|
|
Status string `json:"status,omitempty"`
|
|
}
|
|
|
|
// OutputText 拍平 function_call_output 的 output(string 或部件数组)。
|
|
func (it RespItem) OutputText() string {
|
|
if len(it.Output) == 0 {
|
|
return ""
|
|
}
|
|
var s string
|
|
if err := json.Unmarshal(it.Output, &s); err == nil {
|
|
return s
|
|
}
|
|
var parts []RespPart
|
|
if err := json.Unmarshal(it.Output, &parts); err != nil {
|
|
return string(it.Output)
|
|
}
|
|
texts := make([]string, 0, len(parts))
|
|
for _, p := range parts {
|
|
texts = append(texts, p.Text)
|
|
}
|
|
return strings.Join(texts, "\n")
|
|
}
|
|
|
|
// RespContent 兼容 string 与 part 数组两种形态。
|
|
type RespContent struct {
|
|
Text string
|
|
Parts []RespPart
|
|
IsArray bool
|
|
}
|
|
|
|
func (c *RespContent) UnmarshalJSON(b []byte) error {
|
|
t := strings.TrimSpace(string(b))
|
|
if strings.HasPrefix(t, "\"") {
|
|
return json.Unmarshal(b, &c.Text)
|
|
}
|
|
c.IsArray = true
|
|
return json.Unmarshal(b, &c.Parts)
|
|
}
|
|
|
|
// JoinText 拍平内容为纯文本(input_text / output_text / refusal)。
|
|
func (c RespContent) JoinText() string {
|
|
if !c.IsArray {
|
|
return c.Text
|
|
}
|
|
texts := make([]string, 0, len(c.Parts))
|
|
for _, p := range c.Parts {
|
|
if p.Text != "" {
|
|
texts = append(texts, p.Text)
|
|
}
|
|
}
|
|
return strings.Join(texts, "\n")
|
|
}
|
|
|
|
// HasUnsupported 报告是否含网关无法承接的部件(文本 / input_image 之外,或依赖服务端文件存储)。
|
|
func (c RespContent) HasUnsupported() bool {
|
|
for _, p := range c.Parts {
|
|
switch p.Type {
|
|
case "", "input_text", "output_text", "text", "refusal":
|
|
case "input_image":
|
|
if p.ImageURL == "" || p.FileID != "" {
|
|
return true
|
|
}
|
|
default:
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// RespPart 是 content 数组里的一个部件;input_image 的 image_url 为字符串(URL 或 data URI)。
|
|
type RespPart struct {
|
|
Type string `json:"type"`
|
|
Text string `json:"text,omitempty"`
|
|
ImageURL string `json:"image_url,omitempty"`
|
|
Detail string `json:"detail,omitempty"`
|
|
FileID string `json:"file_id,omitempty"`
|
|
}
|
|
|
|
// RespTool 是 Responses 扁平工具定义(仅支持 type=function)。
|
|
type RespTool struct {
|
|
Type string `json:"type"`
|
|
Name string `json:"name,omitempty"`
|
|
Description string `json:"description,omitempty"`
|
|
Parameters json.RawMessage `json:"parameters,omitempty"`
|
|
Strict *bool `json:"strict,omitempty"`
|
|
}
|
|
|
|
// RespText 收纳输出格式配置(text.format)。
|
|
type RespText struct {
|
|
Format *RespTextFormat `json:"format,omitempty"`
|
|
}
|
|
|
|
// RespTextFormat 是扁平的 format 对象(text / json_object / json_schema)。
|
|
type RespTextFormat struct {
|
|
Type string `json:"type"`
|
|
Name string `json:"name,omitempty"`
|
|
Schema json.RawMessage `json:"schema,omitempty"`
|
|
Strict *bool `json:"strict,omitempty"`
|
|
}
|
|
|
|
// RespReasoning 只透传 effort;summary 等忽略。
|
|
type RespReasoning struct {
|
|
Effort string `json:"effort,omitempty"`
|
|
}
|
|
|
|
// RespUsage 是 Responses 口径的用量(input/output 命名)。
|
|
type RespUsage struct {
|
|
InputTokens int `json:"input_tokens"`
|
|
OutputTokens int `json:"output_tokens"`
|
|
TotalTokens int `json:"total_tokens"`
|
|
InputTokensDetails RespInDetails `json:"input_tokens_details"`
|
|
OutputTokensDetails RespOutDetails `json:"output_tokens_details"`
|
|
}
|
|
|
|
// RespInDetails / RespOutDetails 恒零值输出(OCI 不提供细分)。
|
|
type RespInDetails struct {
|
|
CachedTokens int `json:"cached_tokens"`
|
|
}
|
|
|
|
type RespOutDetails struct {
|
|
ReasoningTokens int `json:"reasoning_tokens"`
|
|
}
|
|
|
|
// RespError 是 Response.error 字段。
|
|
type RespError struct {
|
|
Code string `json:"code"`
|
|
Message string `json:"message"`
|
|
}
|
|
|
|
// RespIncomplete 说明 status=incomplete 的原因。
|
|
type RespIncomplete struct {
|
|
Reason string `json:"reason"`
|
|
}
|
|
|
|
// RespResponse 描述 /ai/v1/responses 非流式响应的常用顶层字段(文档用途)。
|
|
// 直通端点原样返回上游 JSON,未列字段(reasoning、incomplete_details 等)同样保留。
|
|
type RespResponse struct {
|
|
ID string `json:"id"`
|
|
Object string `json:"object"`
|
|
CreatedAt int64 `json:"created_at"`
|
|
Status string `json:"status"`
|
|
Model string `json:"model"`
|
|
Output json.RawMessage `json:"output"`
|
|
Usage *RespUsage `json:"usage,omitempty"`
|
|
Error *RespError `json:"error,omitempty"`
|
|
}
|