Files
2026-06-17 11:29:51 +08:00

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
}