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"` }