refactor: 重构Gemini适配器以支持多模态输入 fix: 修复React Hooks依赖警告 style: 清理未使用的导入和代码 docs: 更新用户界面文本和提示 perf: 优化图像和视频URL处理性能 test: 添加数据迁移工具和测试 build: 更新依赖项和.gitignore chore: 同步Zenmux模型和价格比例
123 lines
3.1 KiB
Go
123 lines
3.1 KiB
Go
package model
|
|
|
|
type Message struct {
|
|
Role string `json:"role,omitempty"`
|
|
Content any `json:"content,omitempty"`
|
|
ReasoningContent any `json:"reasoning_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 {
|
|
for _, contentItem := range anyList {
|
|
contentMap, ok := contentItem.(map[string]any)
|
|
if !ok {
|
|
continue
|
|
}
|
|
switch contentMap["type"] {
|
|
case ContentTypeText:
|
|
if subStr, ok := contentMap["text"].(string); ok {
|
|
contentList = append(contentList, MessageContent{
|
|
Type: ContentTypeText,
|
|
Text: subStr,
|
|
})
|
|
}
|
|
case ContentTypeImageURL:
|
|
if subObj, ok := contentMap["image_url"].(map[string]any); ok {
|
|
contentList = append(contentList, MessageContent{
|
|
Type: ContentTypeImageURL,
|
|
ImageURL: &ImageURL{
|
|
Url: subObj["url"].(string),
|
|
},
|
|
})
|
|
}
|
|
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},
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return contentList
|
|
}
|
|
return nil
|
|
}
|
|
|
|
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"`
|
|
}
|