Like-api/relay/model/message.go
hjjjj a9a224e7dd
Some checks failed
CI / Unit tests (push) Has been cancelled
CI / commit_lint (push) Has been cancelled
feat: add model configuration management and logging enhancements
- Introduced a new ModelConfigEditor component for managing model configurations.
- Added API endpoints for model configuration management, including fetching, saving, and importing/exporting configurations.
- Enhanced logging for request bodies, particularly for multimodal requests containing image URLs.
- Updated the .gitignore to include new configuration files.
- Refactored model handling in the controller to support custom model configurations.
2026-04-08 16:33:27 +08:00

154 lines
4.4 KiB
Go

package model
import (
"github.com/songquanpeng/one-api/common/logger"
)
type Message struct {
Role string `json:"role,omitempty"`
Content any `json:"content,omitempty"`
ReasoningContent any `json:"reasoning_content,omitempty"`
ReasoningEncryptedContent string `json:"reasoning_encrypted_content,omitempty"`
Name *string `json:"name,omitempty"`
ToolCalls []Tool `json:"tool_calls,omitempty"`
ToolCallId string `json:"tool_call_id,omitempty"`
}
func (m Message) IsStringContent() bool {
_, ok := m.Content.(string)
return ok
}
func (m Message) StringContent() string {
content, ok := m.Content.(string)
if ok {
return content
}
contentList, ok := m.Content.([]any)
if ok {
var contentStr string
for _, contentItem := range contentList {
contentMap, ok := contentItem.(map[string]any)
if !ok {
continue
}
if contentMap["type"] == ContentTypeText {
if subStr, ok := contentMap["text"].(string); ok {
contentStr += subStr
}
}
}
return contentStr
}
return ""
}
func (m Message) ParseContent() []MessageContent {
var contentList []MessageContent
content, ok := m.Content.(string)
if ok {
contentList = append(contentList, MessageContent{
Type: ContentTypeText,
Text: content,
})
return contentList
}
anyList, ok := m.Content.([]any)
if ok {
logger.Debugf(nil, "=== ParseContent: Found array content with %d items ===", len(anyList))
for i, contentItem := range anyList {
contentMap, ok := contentItem.(map[string]any)
if !ok {
logger.Debugf(nil, "=== ParseContent: Item %d is not a map, type=%T ===", i, contentItem)
continue
}
logger.Debugf(nil, "=== ParseContent: Item %d has type=%v ===", i, contentMap["type"])
switch contentMap["type"] {
case ContentTypeText:
if subStr, ok := contentMap["text"].(string); ok {
contentList = append(contentList, MessageContent{
Type: ContentTypeText,
Text: subStr,
})
}
case ContentTypeImageURL:
logger.Debugf(nil, "=== ParseContent: Processing image_url item ===")
if subObj, ok := contentMap["image_url"].(map[string]any); ok {
if url, ok := subObj["url"].(string); ok {
logger.Debugf(nil, "=== ParseContent: Found image URL: %s ===", truncateURL(url))
contentList = append(contentList, MessageContent{
Type: ContentTypeImageURL,
ImageURL: &ImageURL{
Url: url,
},
})
} else {
logger.Errorf(nil, "=== ParseContent: image_url.url is not a string, type=%T ===", subObj["url"])
}
} else {
logger.Errorf(nil, "=== ParseContent: image_url is not a map, type=%T ===", contentMap["image_url"])
}
case ContentTypeVideoURL:
if subObj, ok := contentMap["video_url"].(map[string]any); ok {
if url, ok := subObj["url"].(string); ok {
contentList = append(contentList, MessageContent{
Type: ContentTypeVideoURL,
VideoURL: &VideoURL{Url: url},
})
}
}
case ContentTypeInputAudio:
if subObj, ok := contentMap["input_audio"].(map[string]any); ok {
data, _ := subObj["data"].(string)
format, _ := subObj["format"].(string)
if data != "" {
contentList = append(contentList, MessageContent{
Type: ContentTypeInputAudio,
InputAudio: &InputAudio{Data: data, Format: format},
})
}
}
}
}
imageCount := 0
for _, c := range contentList {
if c.Type == ContentTypeImageURL {
imageCount++
}
}
logger.Debugf(nil, "=== ParseContent: Parsed %d content items, image_count=%d ===", len(contentList), imageCount)
return contentList
}
logger.Debugf(nil, "=== ParseContent: Content is neither string nor array, type=%T ===", m.Content)
return nil
}
func truncateURL(url string) string {
if len(url) > 100 {
return url[:100] + "..."
}
return url
}
type ImageURL struct {
Url string `json:"url,omitempty"`
Detail string `json:"detail,omitempty"`
}
type VideoURL struct {
Url string `json:"url,omitempty"`
}
type InputAudio struct {
Data string `json:"data,omitempty"` // base64-encoded audio (no data: prefix)
Format string `json:"format,omitempty"` // e.g. "mp3", "wav", "webm", "ogg"
}
type MessageContent struct {
Type string `json:"type,omitempty"`
Text string `json:"text"`
ImageURL *ImageURL `json:"image_url,omitempty"`
VideoURL *VideoURL `json:"video_url,omitempty"`
InputAudio *InputAudio `json:"input_audio,omitempty"`
}