ai-css/library/modelprovider/dto.go
2026-02-12 08:50:11 +00:00

257 lines
6.4 KiB
Go
Executable File
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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_bytesinline 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
}