/* * Copyright 2025 coze-dev 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 conversation import ( "context" "encoding/json" "strconv" "github.com/coze-dev/coze-studio/backend/api/model/conversation/message" "github.com/coze-dev/coze-studio/backend/api/model/conversation/run" apiMessage "github.com/coze-dev/coze-studio/backend/api/model/crossdomain/message" message3 "github.com/coze-dev/coze-studio/backend/api/model/crossdomain/message" "github.com/coze-dev/coze-studio/backend/application/base/ctxutil" convEntity "github.com/coze-dev/coze-studio/backend/domain/conversation/conversation/entity" "github.com/coze-dev/coze-studio/backend/domain/conversation/message/entity" uploadService "github.com/coze-dev/coze-studio/backend/domain/upload/service" "github.com/coze-dev/coze-studio/backend/pkg/errorx" "github.com/coze-dev/coze-studio/backend/pkg/lang/ptr" "github.com/coze-dev/coze-studio/backend/pkg/lang/slices" "github.com/coze-dev/coze-studio/backend/types/errno" ) type OpenapiMessageApplication struct { UploaodDomainSVC uploadService.UploadService } var OpenapiMessageSVC = new(OpenapiMessageApplication) func (m *OpenapiMessageApplication) GetApiMessageList(ctx context.Context, mr *message.ListMessageApiRequest) (*message.ListMessageApiResponse, error) { // Get Conversation ID by agent id & userID & scene userID := ctxutil.MustGetUIDFromApiAuthCtx(ctx) currentConversation, err := getConversation(ctx, mr.ConversationID) if err != nil { return nil, err } if currentConversation == nil { return nil, errorx.New(errno.ErrConversationNotFound) } if currentConversation.CreatorID != userID { return nil, errorx.New(errno.ErrConversationPermissionCode, errorx.KV("msg", "permission denied")) } if mr.Limit == nil { mr.Limit = ptr.Of(int64(50)) } msgListMeta := &entity.ListMeta{ ConversationID: currentConversation.ID, AgentID: currentConversation.AgentID, Limit: int(ptr.From(mr.Limit)), MessageType: []*message3.MessageType{ ptr.Of(message3.MessageTypeQuestion), ptr.Of(message3.MessageTypeAnswer), }, } if mr.ChatID != nil { msgListMeta.RunID = []*int64{mr.ChatID} } if mr.BeforeID != nil { msgListMeta.Direction = entity.ScrollPageDirectionNext msgListMeta.Cursor = *mr.BeforeID } else { msgListMeta.Direction = entity.ScrollPageDirectionPrev msgListMeta.Cursor = ptr.From(mr.AfterID) } if mr.Order == nil { msgListMeta.OrderBy = ptr.Of(message.OrderByDesc) } else { msgListMeta.OrderBy = mr.Order } mListMessages, err := ConversationSVC.MessageDomainSVC.ListWithoutPair(ctx, msgListMeta) if err != nil { return nil, err } resp := m.buildMessageListResponse(ctx, mListMessages, currentConversation) return resp, nil } func getConversation(ctx context.Context, conversationID int64) (*convEntity.Conversation, error) { conversationInfo, err := ConversationSVC.ConversationDomainSVC.GetByID(ctx, conversationID) if err != nil { return nil, err } return conversationInfo, nil } func (m *OpenapiMessageApplication) buildMessageListResponse(ctx context.Context, mListMessages *entity.ListResult, currentConversation *convEntity.Conversation) *message.ListMessageApiResponse { messagesVO := slices.Transform(mListMessages.Messages, func(dm *entity.Message) *message.OpenMessageApi { content := dm.Content msg := &message.OpenMessageApi{ ID: dm.ID, ConversationID: dm.ConversationID, BotID: dm.AgentID, Role: string(dm.Role), Type: string(dm.MessageType), Content: content, ContentType: string(dm.ContentType), SectionID: strconv.FormatInt(dm.SectionID, 10), CreatedAt: dm.CreatedAt / 1000, UpdatedAt: dm.UpdatedAt / 1000, ChatID: dm.RunID, MetaData: dm.Ext, ReasoningContent: ptr.Of(dm.ReasoningContent), } if dm.ContentType == message3.ContentTypeMix && dm.DisplayContent != "" { msg.Content = m.parseDisplayContent(ctx, dm) msg.ContentType = run.ContentTypeMixApi } return msg }) resp := &message.ListMessageApiResponse{ Messages: messagesVO, HasMore: ptr.Of(mListMessages.HasMore), FirstID: ptr.Of(mListMessages.PrevCursor), LastID: ptr.Of(mListMessages.NextCursor), } return resp } func (m *OpenapiMessageApplication) parseDisplayContent(ctx context.Context, dm *entity.Message) string { var inputs []*run.AdditionalContent err := json.Unmarshal([]byte(dm.DisplayContent), &inputs) if err != nil { return dm.DisplayContent } for k, one := range inputs { if one == nil { continue } switch apiMessage.InputType(one.Type) { case apiMessage.InputTypeText: continue case apiMessage.InputTypeImage, apiMessage.InputTypeFile: if one.GetFileID() != 0 { fileInfo, err := m.UploaodDomainSVC.GetFile(ctx, &uploadService.GetFileRequest{ ID: one.GetFileID(), }) if err == nil { inputs[k].FileURL = ptr.Of(fileInfo.File.Url) } } default: continue } } content, err := json.Marshal(inputs) if err == nil { dm.DisplayContent = string(content) } return dm.DisplayContent }