125 lines
3.5 KiB
Go
125 lines
3.5 KiB
Go
package message
|
|
|
|
import (
|
|
"errors"
|
|
"net/url"
|
|
"strings"
|
|
|
|
"github.com/volcengine/volcengine-go-sdk/service/arkruntime/model"
|
|
)
|
|
|
|
func BuildArkMessages(chatMessages []ChatMessage) ([]*model.ChatCompletionMessage, error) {
|
|
messages := make([]*model.ChatCompletionMessage, 0, len(chatMessages))
|
|
for _, m := range chatMessages {
|
|
msg, err := BuildArkMessage(m)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
messages = append(messages, msg)
|
|
}
|
|
return messages, nil
|
|
}
|
|
|
|
func HasImageMessage(messages []ChatMessage) bool {
|
|
for _, msg := range messages {
|
|
if strings.TrimSpace(msg.ImageURL) != "" || strings.TrimSpace(msg.ImageURLAlias) != "" {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func BuildToolDecisionMessages(chatMessages []ChatMessage) ([]*model.ChatCompletionMessage, error) {
|
|
messages := make([]*model.ChatCompletionMessage, 0, len(chatMessages))
|
|
for _, m := range chatMessages {
|
|
content := m.Content
|
|
if strings.TrimSpace(m.ImageURL) != "" || strings.TrimSpace(m.ImageURLAlias) != "" {
|
|
content = strings.TrimSpace(content)
|
|
placeholder := "[用户上传了一张图片。工具判断阶段不读取图片内容;如果问题主要依赖识图,应不要调用工具,交给最终多模态模型回答。]"
|
|
if content == "" {
|
|
content = placeholder
|
|
} else {
|
|
content += "\n\n" + placeholder
|
|
}
|
|
}
|
|
messages = append(messages, &model.ChatCompletionMessage{Role: m.Role, Content: StringContent(content)})
|
|
}
|
|
return messages, nil
|
|
}
|
|
|
|
func BuildArkMessage(m ChatMessage) (*model.ChatCompletionMessage, error) {
|
|
msg := &model.ChatCompletionMessage{Role: m.Role}
|
|
|
|
if m.ImageURL == "" && m.ImageURLAlias != "" {
|
|
m.ImageURL = m.ImageURLAlias
|
|
}
|
|
|
|
if m.ImageURL == "" {
|
|
msg.Content = &model.ChatCompletionMessageContent{
|
|
StringValue: &m.Content,
|
|
}
|
|
return msg, nil
|
|
}
|
|
|
|
imageURL, err := NormalizeImageURL(m.ImageURL)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// 有图片时:文字内容可有可无(图片 caption 场景),均构造多模态消息
|
|
// 若无文字,则只传图片 part;若同时有图片和文字,先文后图
|
|
parts := make([]*model.ChatCompletionMessageContentPart, 0, 2)
|
|
if m.Content != "" {
|
|
parts = append(parts, TextPart(m.Content))
|
|
}
|
|
parts = append(parts, ImagePart(imageURL))
|
|
msg.Content = &model.ChatCompletionMessageContent{ListValue: parts}
|
|
return msg, nil
|
|
}
|
|
|
|
func ImagePart(url string) *model.ChatCompletionMessageContentPart {
|
|
return &model.ChatCompletionMessageContentPart{
|
|
Type: model.ChatCompletionMessageContentPartTypeImageURL,
|
|
ImageURL: &model.ChatMessageImageURL{
|
|
URL: url,
|
|
Detail: model.ImageURLDetailAuto,
|
|
},
|
|
}
|
|
}
|
|
|
|
func TextPart(text string) *model.ChatCompletionMessageContentPart {
|
|
return &model.ChatCompletionMessageContentPart{
|
|
Type: model.ChatCompletionMessageContentPartTypeText,
|
|
Text: text,
|
|
}
|
|
}
|
|
|
|
func StringContent(text string) *model.ChatCompletionMessageContent {
|
|
return &model.ChatCompletionMessageContent{StringValue: &text}
|
|
}
|
|
|
|
func ChatMessageContentString(content *model.ChatCompletionMessageContent) string {
|
|
if content == nil || content.StringValue == nil {
|
|
return ""
|
|
}
|
|
return *content.StringValue
|
|
}
|
|
|
|
func NormalizeImageURL(raw string) (string, error) {
|
|
raw = strings.TrimSpace(raw)
|
|
if raw == "" {
|
|
return "", errors.New("图片地址不能为空")
|
|
}
|
|
|
|
lower := strings.ToLower(raw)
|
|
if strings.HasPrefix(lower, "data:") {
|
|
return normalizeImageDataURI(raw)
|
|
}
|
|
|
|
u, err := url.Parse(raw)
|
|
if err != nil || u.Host == "" || (u.Scheme != "http" && u.Scheme != "https") {
|
|
return "", errors.New("图片地址无效,仅支持 http/https URL 或 base64 data URI")
|
|
}
|
|
return raw, nil
|
|
}
|