Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 145 additions & 14 deletions callbacks/cozeloop/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ package cozeloop

import (
"context"
"fmt"
"log"

"github.com/bytedance/sonic"
"github.com/coze-dev/cozeloop-go/spec/tracespec"
Expand Down Expand Up @@ -76,21 +78,16 @@ func convertModelMessage(message *schema.Message) *tracespec.ModelMessage {
ToolCallID: message.ToolCallID,
ReasoningContent: message.ReasoningContent,
}
if message.Role == schema.Tool {
msg.Name = message.ToolName
}

for i := range message.MultiContent {
part := message.MultiContent[i]

msg.Parts[i] = &tracespec.ModelMessagePart{
Type: tracespec.ModelMessagePartType(part.Type),
Text: part.Text,
}

if part.ImageURL != nil {
msg.Parts[i].ImageURL = &tracespec.ModelImageURL{
URL: part.ImageURL.URL,
Detail: string(part.ImageURL.Detail),
}
}
if len(message.UserInputMultiContent) > 0 {
msg.Parts = convertUserInputMultiContent(message.UserInputMultiContent)
} else if len(message.AssistantGenMultiContent) > 0 {
msg.Parts = convertAssistantGenMultiContent(message.AssistantGenMultiContent)
} else {
msg.Parts = convertMultiContent(message.MultiContent)
}

for i := range message.ToolCalls {
Expand Down Expand Up @@ -118,6 +115,140 @@ func convertModelMessage(message *schema.Message) *tracespec.ModelMessage {
return msg
}

func convertUserInputMultiContent(parts []schema.MessageInputPart) []*tracespec.ModelMessagePart {
var result []*tracespec.ModelMessagePart
for _, part := range parts {
sign := GetBase64ThoughtSignatureFromExtra(part.Extra)
switch part.Type {
case schema.ChatMessagePartTypeText:
result = append(result, &tracespec.ModelMessagePart{
Type: tracespec.ModelMessagePartType(part.Type),
Text: part.Text,
Signature: sign,
})

case schema.ChatMessagePartTypeImageURL:
if part.Image == nil {
continue
}

if part.Image.MessagePartCommon.URL != nil {
result = append(result, &tracespec.ModelMessagePart{
Type: tracespec.ModelMessagePartType(part.Type),
ImageURL: &tracespec.ModelImageURL{
URL: *part.Image.MessagePartCommon.URL,
Detail: string(part.Image.Detail),
},
Signature: sign,
})
}
if part.Image.MessagePartCommon.Base64Data != nil {
result = append(result, &tracespec.ModelMessagePart{
Type: tracespec.ModelMessagePartType(part.Type),
ImageURL: &tracespec.ModelImageURL{
URL: fmt.Sprintf("data:%s;base64,%s", part.Image.MessagePartCommon.MIMEType, *part.Image.MessagePartCommon.Base64Data),
Detail: string(part.Image.Detail),
},
Signature: sign,
})
}

case schema.ChatMessagePartTypeFileURL:
if part.File == nil {
continue
}
if part.File.MessagePartCommon.URL != nil {
result = append(result, &tracespec.ModelMessagePart{
Type: tracespec.ModelMessagePartType(part.Type),
FileURL: &tracespec.ModelFileURL{
URL: *part.File.MessagePartCommon.URL,
},
Signature: sign,
})
}
if part.File.MessagePartCommon.Base64Data != nil {
result = append(result, &tracespec.ModelMessagePart{
Type: tracespec.ModelMessagePartType(part.Type),
FileURL: &tracespec.ModelFileURL{
URL: fmt.Sprintf("data:%s;base64,%s", part.File.MessagePartCommon.MIMEType, *part.File.MessagePartCommon.Base64Data),
},
Signature: sign,
})
}

default:
log.Printf("unknown part type: %s", part.Type)
}
}
return result
}

func convertAssistantGenMultiContent(parts []schema.MessageOutputPart) []*tracespec.ModelMessagePart {
var result []*tracespec.ModelMessagePart
for _, part := range parts {
sign := GetBase64ThoughtSignatureFromExtra(part.Extra)
switch part.Type {
case schema.ChatMessagePartTypeText:
result = append(result, &tracespec.ModelMessagePart{
Type: tracespec.ModelMessagePartType(part.Type),
Text: part.Text,
Signature: sign,
})
case schema.ChatMessagePartTypeImageURL:
if part.Image == nil {
continue
}
if part.Image.MessagePartCommon.URL != nil {
result = append(result, &tracespec.ModelMessagePart{
Type: tracespec.ModelMessagePartType(part.Type),
ImageURL: &tracespec.ModelImageURL{
URL: *part.Image.MessagePartCommon.URL,
},
Signature: sign,
})
}
if part.Image.MessagePartCommon.Base64Data != nil {
result = append(result, &tracespec.ModelMessagePart{
Type: tracespec.ModelMessagePartType(part.Type),
ImageURL: &tracespec.ModelImageURL{
URL: *part.Image.MessagePartCommon.Base64Data,
},
Signature: sign,
})
}
default:
log.Printf("unknown part type: %s", part.Type)
}
}
return result
}

func convertMultiContent(parts []schema.ChatMessagePart) []*tracespec.ModelMessagePart {
result := make([]*tracespec.ModelMessagePart, len(parts))
for i := range parts {
part := parts[i]

result[i] = &tracespec.ModelMessagePart{
Type: tracespec.ModelMessagePartType(part.Type),
Text: part.Text,
}

if part.ImageURL != nil {
result[i].ImageURL = &tracespec.ModelImageURL{
URL: part.ImageURL.URL,
Detail: string(part.ImageURL.Detail),
}
}

if part.FileURL != nil {
result[i].FileURL = &tracespec.ModelFileURL{
URL: part.FileURL.URL,
}
}
}
return result
}

func addToolName(ctx context.Context, message *tracespec.ModelMessage) *tracespec.ModelMessage {
if message == nil {
return message
Expand Down
8 changes: 5 additions & 3 deletions callbacks/cozeloop/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ go 1.23.0
require (
github.com/bytedance/mockey v1.2.14
github.com/bytedance/sonic v1.14.1
github.com/cloudwego/eino v0.6.0
github.com/cloudwego/eino v0.7.21
github.com/coze-dev/cozeloop-go v0.1.17
github.com/coze-dev/cozeloop-go/spec v0.1.7
github.com/coze-dev/cozeloop-go/spec v0.1.8
github.com/smartystreets/goconvey v1.8.1
github.com/stretchr/testify v1.10.0
)

require (
Expand All @@ -20,8 +21,9 @@ require (
github.com/cloudwego/base64x v0.1.6 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/eino-contrib/jsonschema v1.0.2 // indirect
github.com/eino-contrib/jsonschema v1.0.3 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/goph/emperror v0.17.2 // indirect
github.com/gopherjs/gopherjs v1.17.2 // indirect
github.com/json-iterator/go v1.1.12 // indirect
Expand Down
45 changes: 27 additions & 18 deletions callbacks/cozeloop/go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
github.com/airbrake/gobrake v3.6.1+incompatible/go.mod h1:wM4gu3Cn0W0K7GUuVWnlXZU11AGBXMILnrdOU8Kn00o=
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
Expand All @@ -20,36 +22,40 @@ github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFos
github.com/certifi/gocertifi v0.0.0-20190105021004-abcd57078448/go.mod h1:GJKEexRPVJrBSOjoqN5VNOIKJ5Q3RViH6eu3puDRwx4=
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
github.com/cloudwego/eino v0.6.0 h1:pobGKMOfcQHVNhD9UT/HrvO0eYG6FC2ML/NKY2Eb9+Q=
github.com/cloudwego/eino v0.6.0/go.mod h1:JNapfU+QUrFFpboNDrNOFvmz0m9wjBFHHCr77RH6a50=
github.com/coze-dev/cozeloop-go v0.1.7 h1:Y9AbLFLJdYWGPJJecWScSeW8W4kY38CLzBx0/ha/lZ4=
github.com/coze-dev/cozeloop-go v0.1.7/go.mod h1:rhHtKT9D8wdqd+X1heP2A7zNyTohuA16ESv+rhEClbE=
github.com/cloudwego/eino v0.7.21 h1:kkq7hlHYzwkGOAMbY4ffym4oBT7e9g5hXpJTsZbhsik=
github.com/cloudwego/eino v0.7.21/go.mod h1:nA8Vacmuqv3pqKBQbTWENBLQ8MmGmPt/WqiyLeB8ohQ=
github.com/coze-dev/cozeloop-go v0.1.17 h1:oUd5B7O7BuaFgp8V9JtLui9YfIsDw0jFit8BsII4/qM=
github.com/coze-dev/cozeloop-go v0.1.17/go.mod h1:lM7cmUEZlnAlQYdwfk4Li0SC3RdZ++QMHX75nvKceSc=
github.com/coze-dev/cozeloop-go/spec v0.1.5 h1:tEQ82qlz9/HZv8MqyZq+043SaHs5C44MWslyGm5UcNI=
github.com/coze-dev/cozeloop-go/spec v0.1.5/go.mod h1:/f3BrWehffwXIpd4b5rYIqktLd/v5dlLBw0h9F/LQIU=
github.com/coze-dev/cozeloop-go/spec v0.1.7-0.20251124063846-a86c0c533c17 h1:pB3WAXqZr4GE2sQgLmCXIFeVaRVWz1UYP9J2E6LnL00=
github.com/coze-dev/cozeloop-go/spec v0.1.7-0.20251124063846-a86c0c533c17/go.mod h1:/f3BrWehffwXIpd4b5rYIqktLd/v5dlLBw0h9F/LQIU=
github.com/coze-dev/cozeloop-go/spec v0.1.7 h1:8/w1mVUQg3vVbkmqoF/KczTNjd8y3JsQ0Lx1JOE2Lhg=
github.com/coze-dev/cozeloop-go/spec v0.1.7/go.mod h1:/f3BrWehffwXIpd4b5rYIqktLd/v5dlLBw0h9F/LQIU=
github.com/coze-dev/cozeloop-go/spec v0.1.8 h1:hFVBj/C1B6mUNGH/q52kO2n1pXuTomG578RbKlfYLGk=
github.com/coze-dev/cozeloop-go/spec v0.1.8/go.mod h1:/f3BrWehffwXIpd4b5rYIqktLd/v5dlLBw0h9F/LQIU=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/eino-contrib/jsonschema v1.0.2 h1:HaxruBMUdnXa7Lg/lX8g0Hk71ZIfdTZXmBQz0e3esr8=
github.com/eino-contrib/jsonschema v1.0.2/go.mod h1:cpnX4SyKjWjGC7iN2EbhxaTdLqGjCi0e9DxpLYxddD4=
github.com/eino-contrib/jsonschema v1.0.3 h1:2Kfsm1xlMV0ssY2nuxshS4AwbLFuqmPmzIjLVJ1Fsp0=
github.com/eino-contrib/jsonschema v1.0.3/go.mod h1:cpnX4SyKjWjGC7iN2EbhxaTdLqGjCi0e9DxpLYxddD4=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
github.com/go-check/check v0.0.0-20180628173108-788fd7840127 h1:0gkP6mzaMqkmpcJYCFOLkIBwI7xFExG03bbkOkCvUPI=
github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/goph/emperror v0.17.2 h1:yLapQcmEsO0ipe9p5TaN22djm3OFV/TfM/fcYP0/J18=
github.com/goph/emperror v0.17.2/go.mod h1:+ZbQ+fUNO/6FNiUo0ujtMjhgad9Xa6fQL9KhH4LNHic=
github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
Expand Down Expand Up @@ -89,12 +95,15 @@ github.com/nikolalohinski/gonja/v2 v2.3.1 h1:UGyLa6NDNq6dCGkFY33sziUssjTdh95xrYs
github.com/nikolalohinski/gonja/v2 v2.3.1/go.mod h1:1Wcc/5huTu6y36e0sOFR1XQoFlylw3c3H3L5WOz0RDg=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU=
github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc=
github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ=
github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N7Xxu0=
github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.2-0.20201214064552-5dd12d0cfe7f h1:lJqhwddJVYAkyp72a4pwzMClI20xTwL7miDdm2W/KBM=
github.com/pkg/errors v0.9.2-0.20201214064552-5dd12d0cfe7f/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
Expand Down Expand Up @@ -142,14 +151,12 @@ golang.org/x/arch v0.11.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 h1:MGwJjxBy0HJshjDNfLsYO8xppfqWlA5ZT9OhtUUhTNw=
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 h1:985EYyeCOxTpcgOTJpflJUwOeEz0CQOdPt73OzpE9F8=
golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
Expand All @@ -164,6 +171,8 @@ golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
Expand Down
83 changes: 83 additions & 0 deletions callbacks/cozeloop/signature.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* Copyright 2025 CloudWeGo Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package cozeloop

import "encoding/base64"

const (
thoughtSignatureKey = "gemini_thought_signature"
)

// GetThoughtSignatureFromExtra
// copy from github.com/cloudwego/eino-ext/components/model/gemini, because it's go version is 1.24. we can't depend on it.
//
// tries to read thought_signature from an Extra map.
//
// thought_signature should be read from:
// - message.AssistantGenMultiContent[i].Extra: thought_signature on each generated output part
// - toolCall.Extra: thought_signature on toolCall
// - message.Extra: thought_signature on generated content (legacy, only used when message.AssistantGenMultiContent are absent)
//
// The returned bool indicates whether thought_signature key exists in Extra.
// The returned []byte is the thought signature if available
func GetThoughtSignatureFromExtra(extra map[string]any) ([]byte, bool) {
if extra == nil {
return nil, false
}

signature, exists := extra[thoughtSignatureKey]
if !exists {
return nil, false
}

switch sig := signature.(type) {
case []byte:
if len(sig) == 0 {
return nil, true
}
return sig, true
case string:
// When marshaling a map[string]any to JSON, a []byte value is encoded as a base64 string.
// After unmarshaling back into map[string]any, the value becomes string.
// Decode it here for compatibility with messages restored from JSON.
if sig == "" {
return nil, true
}
decoded, err := base64.StdEncoding.DecodeString(sig)
if err != nil {
return nil, true
}
if len(decoded) == 0 {
return nil, true
}
return decoded, true
default:
return nil, true
}
}

func GetBase64ThoughtSignatureFromExtra(extra map[string]any) string {
signBytes, ok := GetThoughtSignatureFromExtra(extra)
if !ok {
return ""
}
if signBytes == nil {
return ""
}

return base64.StdEncoding.EncodeToString(signBytes)
}
Loading
Loading