Compare commits

...

3 Commits

Author SHA1 Message Date
Ravens2121
a0c6cffb0d fix(kiro):修复 base64 图片格式转换问题 (#10) 2025-12-07 21:55:13 +08:00
Luis Pater
f81ff16022 Merge pull request #8 from Ravens2121/main
feat: 添加Kiro渠道图片支持功能,借鉴justlovemaki/AIClient-2-API实现
2025-12-07 20:18:31 +08:00
Your Name
68cbe20664 feat: 添加Kiro渠道图片支持功能,借鉴justlovemaki/AIClient-2-API实现 2025-12-07 17:56:06 +08:00
2 changed files with 116 additions and 16 deletions

View File

@@ -604,10 +604,22 @@ type kiroHistoryMessage struct {
AssistantResponseMessage *kiroAssistantResponseMessage `json:"assistantResponseMessage,omitempty"`
}
// kiroImage represents an image in Kiro API format
type kiroImage struct {
Format string `json:"format"`
Source kiroImageSource `json:"source"`
}
// kiroImageSource contains the image data
type kiroImageSource struct {
Bytes string `json:"bytes"` // base64 encoded image data
}
type kiroUserInputMessage struct {
Content string `json:"content"`
ModelID string `json:"modelId"`
Origin string `json:"origin"`
Images []kiroImage `json:"images,omitempty"`
UserInputMessageContext *kiroUserInputMessageContext `json:"userInputMessageContext,omitempty"`
}
@@ -824,6 +836,7 @@ func (e *KiroExecutor) buildUserMessageStruct(msg gjson.Result, modelID, origin
content := msg.Get("content")
var contentBuilder strings.Builder
var toolResults []kiroToolResult
var images []kiroImage
if content.IsArray() {
for _, part := range content.Array() {
@@ -831,6 +844,25 @@ func (e *KiroExecutor) buildUserMessageStruct(msg gjson.Result, modelID, origin
switch partType {
case "text":
contentBuilder.WriteString(part.Get("text").String())
case "image":
// Extract image data from Claude API format
mediaType := part.Get("source.media_type").String()
data := part.Get("source.data").String()
// Extract format from media_type (e.g., "image/png" -> "png")
format := ""
if idx := strings.LastIndex(mediaType, "/"); idx != -1 {
format = mediaType[idx+1:]
}
if format != "" && data != "" {
images = append(images, kiroImage{
Format: format,
Source: kiroImageSource{
Bytes: data,
},
})
}
case "tool_result":
// Extract tool result for API
toolUseID := part.Get("tool_use_id").String()
@@ -872,11 +904,18 @@ func (e *KiroExecutor) buildUserMessageStruct(msg gjson.Result, modelID, origin
contentBuilder.WriteString(content.String())
}
return kiroUserInputMessage{
userMsg := kiroUserInputMessage{
Content: contentBuilder.String(),
ModelID: modelID,
Origin: origin,
}, toolResults
}
// Add images to message if present
if len(images) > 0 {
userMsg.Images = images
}
return userMsg, toolResults
}
// buildAssistantMessageStruct builds an assistant message with tool uses

View File

@@ -4,6 +4,7 @@ package chat_completions
import (
"bytes"
"encoding/json"
"strings"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
@@ -182,13 +183,43 @@ func ConvertOpenAIRequestToKiro(modelName string, inputRawJSON []byte, stream bo
"text": part.Get("text").String(),
})
} else if partType == "image_url" {
contentParts = append(contentParts, map[string]interface{}{
"type": "image",
"source": map[string]interface{}{
"type": "url",
"url": part.Get("image_url.url").String(),
},
})
imageURL := part.Get("image_url.url").String()
// Check if it's base64 format (data:image/png;base64,xxxxx)
if strings.HasPrefix(imageURL, "data:") {
// Parse data URL format
// Format: data:image/png;base64,xxxxx
commaIdx := strings.Index(imageURL, ",")
if commaIdx != -1 {
// Extract media_type (e.g., "image/png")
header := imageURL[5:commaIdx] // Remove "data:" prefix
mediaType := header
if semiIdx := strings.Index(header, ";"); semiIdx != -1 {
mediaType = header[:semiIdx]
}
// Extract base64 data
base64Data := imageURL[commaIdx+1:]
contentParts = append(contentParts, map[string]interface{}{
"type": "image",
"source": map[string]interface{}{
"type": "base64",
"media_type": mediaType,
"data": base64Data,
},
})
}
} else {
// Regular URL format - keep original logic
contentParts = append(contentParts, map[string]interface{}{
"type": "image",
"source": map[string]interface{}{
"type": "url",
"url": imageURL,
},
})
}
}
}
} else if content.String() != "" {
@@ -214,13 +245,43 @@ func ConvertOpenAIRequestToKiro(modelName string, inputRawJSON []byte, stream bo
"text": part.Get("text").String(),
})
} else if partType == "image_url" {
contentParts = append(contentParts, map[string]interface{}{
"type": "image",
"source": map[string]interface{}{
"type": "url",
"url": part.Get("image_url.url").String(),
},
})
imageURL := part.Get("image_url.url").String()
// Check if it's base64 format (data:image/png;base64,xxxxx)
if strings.HasPrefix(imageURL, "data:") {
// Parse data URL format
// Format: data:image/png;base64,xxxxx
commaIdx := strings.Index(imageURL, ",")
if commaIdx != -1 {
// Extract media_type (e.g., "image/png")
header := imageURL[5:commaIdx] // Remove "data:" prefix
mediaType := header
if semiIdx := strings.Index(header, ";"); semiIdx != -1 {
mediaType = header[:semiIdx]
}
// Extract base64 data
base64Data := imageURL[commaIdx+1:]
contentParts = append(contentParts, map[string]interface{}{
"type": "image",
"source": map[string]interface{}{
"type": "base64",
"media_type": mediaType,
"data": base64Data,
},
})
}
} else {
// Regular URL format - keep original logic
contentParts = append(contentParts, map[string]interface{}{
"type": "image",
"source": map[string]interface{}{
"type": "url",
"url": imageURL,
},
})
}
}
}
claudeMsg["content"] = contentParts