Files
team-api/relaykit/relayconvert/internal/shared/message_mapper.go
T

139 lines
3.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Package shared 提供格式转换相关的可复用映射函数。
// 这些函数从 relay/channel 适配器中抽取出来,已彼此独立。
package shared
import (
"github.com/qianfree/team-api/relaykit/dto"
)
// MapTextContent 从多种内容格式中抽取纯文本内容。
// 未找到文本时返回空字符串。
func MapTextContent(content any) string {
switch v := content.(type) {
case string:
return v
case []dto.ContentPart:
for _, part := range v {
if part.Type == "text" {
return part.Text
}
}
case []any:
for _, item := range v {
if part, ok := item.(dto.ContentPart); ok && part.Type == "text" {
return part.Text
}
}
}
return ""
}
// MapOpenAIContentPartsToClaude 将 OpenAI ContentPart[] 转换为 Claude ContentBlock[]。
// 处理 text、image_url 等多模态内容类型。
func MapOpenAIContentPartsToClaude(parts []dto.ContentPart) []dto.ClaudeContentBlock {
blocks := make([]dto.ClaudeContentBlock, 0, len(parts))
for _, part := range parts {
switch part.Type {
case "text":
text := part.Text
blocks = append(blocks, dto.ClaudeContentBlock{
Type: "text",
Text: &text,
})
case "image_url":
if part.ImageURL != nil {
source := MapOpenAIImageToClaudeSource(*part.ImageURL)
blocks = append(blocks, dto.ClaudeContentBlock{
Type: "image",
Source: &source,
})
}
// 其他内容类型可在此处扩展
}
}
return blocks
}
// MapClaudeContentToOpenAI 将 Claude ContentBlock[] 转换为 OpenAI 消息内容。
// 纯文本时返回单个字符串;多模态时返回 ContentPart[]。
func MapClaudeContentToOpenAI(blocks []dto.ClaudeContentBlock) any {
if len(blocks) == 0 {
return ""
}
// 仅单个文本块时,按字符串返回
if len(blocks) == 1 && blocks[0].Type == "text" {
if blocks[0].Text != nil {
return *blocks[0].Text
}
return ""
}
// 否则作为 ContentPart 数组返回
parts := make([]dto.ContentPart, 0, len(blocks))
for _, block := range blocks {
switch block.Type {
case "text":
text := ""
if block.Text != nil {
text = *block.Text
}
parts = append(parts, dto.ContentPart{
Type: "text",
Text: text,
})
case "image":
if block.Source != nil {
parts = append(parts, dto.ContentPart{
Type: "image_url",
ImageURL: &dto.ImageURL{
URL: block.Source.Data, // Base64 数据 URL
},
})
}
}
}
return parts
}
// MapOpenAIImageToClaudeSource 将 OpenAI ImageURL 转换为 Claude Source 格式。
func MapOpenAIImageToClaudeSource(imageURL dto.ImageURL) dto.ClaudeSource {
source := dto.ClaudeSource{
Type: "base64",
}
// 解析数据 URL:data:image/png;base64,xxxxx
url := imageURL.URL
if len(url) > 5 && url[:5] == "data:" {
// 抽取媒体类型和数据
if idx := findSubstring(url, ";base64,"); idx > 0 {
mediaType := url[5:idx]
data := url[idx+8:]
source.MediaType = mediaType
source.Data = data
}
} else {
// HTTP URL —— Claude 不直接支持,需要自行抓取
source.Type = "url"
source.Data = url
}
return source
}
// findSubstring 是用于查找子串索引的辅助函数
func findSubstring(s, substr string) int {
for i := 0; i <= len(s)-len(substr); i++ {
if s[i:i+len(substr)] == substr {
return i
}
}
return -1
}