mirror of
https://wget.la/https://github.com/leookun/cursor-byok
synced 2026-08-17 19:47:10 +08:00
Support read image
This commit is contained in:
@@ -4,6 +4,7 @@ package execbridge
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
@@ -2285,6 +2286,18 @@ func buildReadMcpResourceCompletedToolCall(argsJSON []byte, result *agentv1.Read
|
||||
}
|
||||
}
|
||||
|
||||
func isSupportedReadImage(data []byte) bool {
|
||||
if len(data) == 0 {
|
||||
return false
|
||||
}
|
||||
switch strings.ToLower(strings.TrimSpace(http.DetectContentType(data))) {
|
||||
case "image/png", "image/jpeg", "image/gif", "image/webp":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// convertReadResultToReadToolResult 把 `ReadResult` 映射为 `ReadToolResult`。
|
||||
func convertReadResultToReadToolResult(result *agentv1.ReadResult) *agentv1.ReadToolResult {
|
||||
if result == nil {
|
||||
@@ -2318,7 +2331,9 @@ func convertReadResultToReadToolResult(result *agentv1.ReadResult) *agentv1.Read
|
||||
if content != "" {
|
||||
toolSuccess.Output = &agentv1.ReadToolSuccess_Content{Content: content}
|
||||
} else if len(data) > 0 {
|
||||
if len(data) > readReplayBinaryLimit {
|
||||
if isSupportedReadImage(data) {
|
||||
toolSuccess.Output = &agentv1.ReadToolSuccess_Data{Data: append([]byte(nil), data...)}
|
||||
} else if len(data) > readReplayBinaryLimit {
|
||||
toolSuccess.ExceededLimit = true
|
||||
toolSuccess.Output = &agentv1.ReadToolSuccess_Content{
|
||||
Content: replayTruncationNotice("Read binary data", readReplayBinaryLimit, 0, len(data)),
|
||||
|
||||
@@ -1126,7 +1126,16 @@ func isAnthropicCacheableBlock(block map[string]any) bool {
|
||||
case contentPartTypeText:
|
||||
return strings.TrimSpace(anthropicStringField(block, "text")) != ""
|
||||
case "tool_result":
|
||||
return strings.TrimSpace(anthropicStringField(block, "content")) != ""
|
||||
switch content := block["content"].(type) {
|
||||
case string:
|
||||
return strings.TrimSpace(content) != ""
|
||||
case []map[string]any:
|
||||
return len(content) > 0
|
||||
case []any:
|
||||
return len(content) > 0
|
||||
default:
|
||||
return false
|
||||
}
|
||||
case "tool_use":
|
||||
return strings.TrimSpace(anthropicStringField(block, "id")) != "" && strings.TrimSpace(anthropicStringField(block, "name")) != ""
|
||||
default:
|
||||
@@ -1168,10 +1177,18 @@ func normalizeAnthropicProviderMessages(input []Message, thinkingEnabled bool, r
|
||||
if toolUseID == "" {
|
||||
return nil, nil, fmt.Errorf("anthropic tool message requires tool_call_id")
|
||||
}
|
||||
var content any = message.Content
|
||||
if hasImageContentParts(message.ContentParts) {
|
||||
contentBlocks, err := anthropicContentBlocks(message)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
content = contentBlocks
|
||||
}
|
||||
pendingToolResults = append(pendingToolResults, map[string]any{
|
||||
"type": "tool_result",
|
||||
"tool_use_id": toolUseID,
|
||||
"content": message.Content,
|
||||
"content": content,
|
||||
})
|
||||
case "user", "assistant":
|
||||
flushToolResults()
|
||||
|
||||
@@ -1967,10 +1967,18 @@ func normalizeOpenAIResponsesInput(messages []Message) (string, []map[string]any
|
||||
}
|
||||
if role == "tool" && strings.TrimSpace(message.ToolCallID) != "" {
|
||||
callID := openAIResponsesToolMessageCallID(message, responsesCallIDs)
|
||||
var output any = openAIResponsesMessageText(message)
|
||||
if hasImageContentParts(message.ContentParts) {
|
||||
content, err := openAIResponsesMessageContent(message, false)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
output = content
|
||||
}
|
||||
items = append(items, map[string]any{
|
||||
"type": "function_call_output",
|
||||
"call_id": callID,
|
||||
"output": openAIResponsesMessageText(message),
|
||||
"output": output,
|
||||
})
|
||||
activeAssistantReasoningKey = ""
|
||||
continue
|
||||
|
||||
Reference in New Issue
Block a user