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"` }