package modelprovider import ( "time" "github.com/openai/openai-go/v3" "github.com/openai/openai-go/v3/responses" "github.com/openai/openai-go/v3/shared" ) type Role string const ( RoleUser Role = "user" RoleAssistant Role = "assistant" RoleSystem Role = "system" ) const ( PartText PartType = "text" PartImage PartType = "image" ) type Message struct { Role Role `json:"role"` Parts []Part `json:"parts,omitempty"` // 多模态分片(任选其一) } type PartType string type Part struct { Type PartType `json:"type"` // 文本 Text string `json:"text,omitempty"` // 图片(任选其一:URL/内联字节/已有文件ID) ImageURL string `json:"image_url,omitempty"` ImageBytes []byte `json:"image_bytes,omitempty"` MIMEType string `json:"mime_type,omitempty"` // "image/png" 等 } type ChatRequest struct { Model shared.ResponsesModel `json:"model"` Messages []Message `json:"messages"` Temperature *float64 `json:"temperature,omitempty"` TopP *float64 `json:"top_p,omitempty"` MaxTokens *int `json:"max_tokens,omitempty"` VendorExtras map[string]any `json:"vendor_extras,omitempty"` RequestID string `json:"request_id,omitempty"` IsStream bool `json:"is_stream,omitempty"` } type Usage struct { PromptTokens int `json:"prompt_tokens"` CompletionTokens int `json:"completion_tokens"` TotalTokens int `json:"total_tokens"` } type AIError struct { Code string `json:"code"` Message string `json:"message"` } type Meta struct { CreatedAt time.Time `json:"created_at"` Vendor string `json:"vendor"` ModelID string `json:"model_id,omitempty"` Extras map[string]string `json:"extras,omitempty"` } type ChatResponse struct { ID string `json:"id"` Model string `json:"model"` Content string `json:"content"` Usage *Usage `json:"usage,omitempty"` Err *AIError `json:"err,omitempty"` Raw any `json:"raw,omitempty"` Meta Meta `json:"meta"` } // 模型信息(面向统一层) type ModelInfo struct { // 逻辑 ID(仅在 RouterProvider 聚合时回填,如 "openai/gpt-4o-2024-08-06") LogicalID string `json:"logical_id,omitempty"` // 供应商真实模型 ID(如 "gpt-4o-2024-08-06") RealID string `json:"real_id"` // 供应商标识(如 "openai") Vendor string `json:"vendor"` // 展示名(可选) DisplayName string `json:"display_name,omitempty"` // 能力信息(按需精简/扩展) ContextWindow int `json:"context_window,omitempty"` // 最大上下文 SupportsStream bool `json:"supports_stream,omitempty"` InputModalities []string `json:"input_modalities,omitempty"` // e.g. ["text","image","audio"] OutputModalities []string `json:"output_modalities,omitempty"` // e.g. ["text","image"] // 定价/地区/版本等(可选) Region string `json:"region,omitempty"` Version string `json:"version,omitempty"` Metadata map[string]string `json:"metadata,omitempty"` // 供应商原始信息(调试/排障) Raw any `json:"raw,omitempty"` } // 便捷构造器(业务层直接用) func NewPartText(s string) Part { return Part{Type: PartText, Text: s} } func NewPartImageURL(u string) Part { return Part{Type: PartImage, ImageURL: u} } func NewPartImageBytes(b []byte, mt string) Part { return Part{Type: PartImage, ImageBytes: b, MIMEType: mt} } func MakeUserMsg(p []Part) Message { return Message{ Role: RoleUser, Parts: p, } } func MakeAssistantMsg(p []Part) Message { return Message{ Role: RoleAssistant, Parts: p, } } func MakeSystemMsg(p []Part) Message { return Message{ Role: RoleSystem, Parts: p, } } func PartsToOpenaiChatPartUnionParam(parts []Part) (result []openai.ChatCompletionContentPartUnionParam) { for _, p := range parts { switch p.Type { case PartText: if p.Text == "" { continue } result = append(result, openai.ChatCompletionContentPartUnionParam{ OfText: &openai.ChatCompletionContentPartTextParam{ Text: p.Text, }, }, ) case PartImage: // 1️⃣ image_url 优先 if p.ImageURL != "" { result = append(result, openai.ChatCompletionContentPartUnionParam{ OfImageURL: &openai.ChatCompletionContentPartImageParam{ ImageURL: openai.ChatCompletionContentPartImageImageURLParam{ URL: p.ImageURL, }, }, }, ) continue } // 2️⃣ image_bytes(inline image) if len(p.ImageBytes) > 0 { result = append(result, openai.ChatCompletionContentPartUnionParam{ OfImageURL: &openai.ChatCompletionContentPartImageParam{ ImageURL: openai.ChatCompletionContentPartImageImageURLParam{ URL: p.ImageURL, }, }, }, ) } } } return result } func PartsToOpenaiChatContentPartTextParam(parts []Part) (result []openai.ChatCompletionContentPartTextParam) { for _, p := range parts { if p.Type != PartText { continue } if p.Text == "" { continue } result = append(result, openai.ChatCompletionContentPartTextParam{ Text: p.Text, }, ) } return result } func PartsToOpenaiChatAssistantMessageParamContentArrayOfContentPartUnion( parts []Part, ) (result []openai.ChatCompletionAssistantMessageParamContentArrayOfContentPartUnion) { for _, p := range parts { switch p.Type { case PartText: if p.Text == "" { continue } result = append(result, openai.ChatCompletionAssistantMessageParamContentArrayOfContentPartUnion{ OfText: &openai.ChatCompletionContentPartTextParam{ Text: p.Text, }, }, ) } } return result } func PartsToResponseInputItemUnionParam(role Role, parts []Part) (result responses.ResponseInputItemUnionParam) { var openaiRole responses.EasyInputMessageRole switch role { case RoleUser: openaiRole = responses.EasyInputMessageRoleUser case RoleAssistant: openaiRole = responses.EasyInputMessageRoleAssistant case RoleSystem: openaiRole = responses.EasyInputMessageRoleSystem } for _, p := range parts { switch p.Type { case PartText: if p.Text == "" { continue } result = responses.ResponseInputItemUnionParam{ OfMessage: &responses.EasyInputMessageParam{Content: responses.EasyInputMessageContentUnionParam{OfString: openai.String(p.Text)}, Role: openaiRole, }} } } return }