You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
293 lines
9.2 KiB
293 lines
9.2 KiB
/*
|
|
* 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"
|
|
"strings"
|
|
|
|
"github.com/coze-dev/coze-studio/backend/api/model/conversation/common"
|
|
"github.com/coze-dev/coze-studio/backend/api/model/conversation/conversation"
|
|
"github.com/coze-dev/coze-studio/backend/application/base/ctxutil"
|
|
agentrun "github.com/coze-dev/coze-studio/backend/domain/conversation/agentrun/service"
|
|
"github.com/coze-dev/coze-studio/backend/domain/conversation/conversation/entity"
|
|
conversationService "github.com/coze-dev/coze-studio/backend/domain/conversation/conversation/service"
|
|
message "github.com/coze-dev/coze-studio/backend/domain/conversation/message/service"
|
|
"github.com/coze-dev/coze-studio/backend/domain/shortcutcmd/service"
|
|
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/consts"
|
|
"github.com/coze-dev/coze-studio/backend/types/errno"
|
|
)
|
|
|
|
type ConversationApplicationService struct {
|
|
appContext *ServiceComponents
|
|
|
|
AgentRunDomainSVC agentrun.Run
|
|
ConversationDomainSVC conversationService.Conversation
|
|
MessageDomainSVC message.Message
|
|
|
|
ShortcutDomainSVC service.ShortcutCmd
|
|
}
|
|
|
|
var ConversationSVC = new(ConversationApplicationService)
|
|
|
|
type OpenapiAgentRunApplication struct {
|
|
ShortcutDomainSVC service.ShortcutCmd
|
|
UploaodDomainSVC uploadService.UploadService
|
|
}
|
|
|
|
var ConversationOpenAPISVC = new(OpenapiAgentRunApplication)
|
|
|
|
func (c *ConversationApplicationService) ClearHistory(ctx context.Context, req *conversation.ClearConversationHistoryRequest) (*conversation.ClearConversationHistoryResponse, error) {
|
|
resp := new(conversation.ClearConversationHistoryResponse)
|
|
|
|
conversationID := req.ConversationID
|
|
|
|
// get conversation
|
|
currentRes, err := c.ConversationDomainSVC.GetByID(ctx, conversationID)
|
|
if err != nil {
|
|
return resp, err
|
|
}
|
|
if currentRes == nil {
|
|
return resp, errorx.New(errno.ErrConversationNotFound)
|
|
}
|
|
// check user
|
|
userID := ctxutil.GetUIDFromCtx(ctx)
|
|
if userID == nil || *userID != currentRes.CreatorID {
|
|
return resp, errorx.New(errno.ErrConversationNotFound, errorx.KV("msg", "user not match"))
|
|
}
|
|
|
|
// delete conversation
|
|
err = c.ConversationDomainSVC.Delete(ctx, conversationID)
|
|
if err != nil {
|
|
return resp, err
|
|
}
|
|
// create new conversation
|
|
convRes, err := c.ConversationDomainSVC.Create(ctx, &entity.CreateMeta{
|
|
AgentID: currentRes.AgentID,
|
|
UserID: currentRes.CreatorID,
|
|
Scene: currentRes.Scene,
|
|
ConnectorID: consts.CozeConnectorID,
|
|
})
|
|
if err != nil {
|
|
return resp, err
|
|
}
|
|
resp.NewSectionID = convRes.SectionID
|
|
return resp, nil
|
|
}
|
|
|
|
func (c *ConversationApplicationService) CreateSection(ctx context.Context, conversationID int64) (int64, error) {
|
|
currentRes, err := c.ConversationDomainSVC.GetByID(ctx, conversationID)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
if currentRes == nil {
|
|
return 0, errorx.New(errno.ErrConversationNotFound, errorx.KV("msg", "conversation not found"))
|
|
}
|
|
var userID int64
|
|
if currentRes.ConnectorID == consts.CozeConnectorID {
|
|
userID = ctxutil.MustGetUIDFromCtx(ctx)
|
|
} else {
|
|
userID = ctxutil.MustGetUIDFromApiAuthCtx(ctx)
|
|
}
|
|
|
|
if userID != currentRes.CreatorID {
|
|
return 0, errorx.New(errno.ErrConversationNotFound, errorx.KV("msg", "user not match"))
|
|
}
|
|
|
|
convRes, err := c.ConversationDomainSVC.NewConversationCtx(ctx, &entity.NewConversationCtxRequest{
|
|
ID: conversationID,
|
|
})
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return convRes.SectionID, nil
|
|
}
|
|
|
|
func (c *ConversationApplicationService) CreateConversation(ctx context.Context, req *conversation.CreateConversationRequest) (*conversation.CreateConversationResponse, error) {
|
|
resp := new(conversation.CreateConversationResponse)
|
|
apiKeyInfo := ctxutil.GetApiAuthFromCtx(ctx)
|
|
userID := apiKeyInfo.UserID
|
|
connectorID := req.GetConnectorId()
|
|
agentID := req.GetBotId()
|
|
if connectorID != consts.WebSDKConnectorID {
|
|
connectorID = apiKeyInfo.ConnectorID
|
|
}
|
|
|
|
conversationData, err := c.ConversationDomainSVC.Create(ctx, &entity.CreateMeta{
|
|
AgentID: agentID,
|
|
UserID: userID,
|
|
ConnectorID: connectorID,
|
|
Scene: common.Scene_SceneOpenApi,
|
|
Ext: parseMetaData(req.MetaData),
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
resp.ConversationData = &conversation.ConversationData{
|
|
Id: conversationData.ID,
|
|
LastSectionID: &conversationData.SectionID,
|
|
ConnectorID: &conversationData.ConnectorID,
|
|
CreatedAt: conversationData.CreatedAt / 1000,
|
|
MetaData: parseExt(conversationData.Ext),
|
|
}
|
|
return resp, nil
|
|
}
|
|
|
|
func parseMetaData(metaData map[string]string) string {
|
|
if metaData == nil {
|
|
return ""
|
|
}
|
|
j, err := json.Marshal(metaData)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
return string(j)
|
|
}
|
|
|
|
func parseExt(ext string) map[string]string {
|
|
if ext == "" {
|
|
return nil
|
|
}
|
|
var metaData map[string]string
|
|
err := json.Unmarshal([]byte(ext), &metaData)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
return metaData
|
|
}
|
|
func (c *ConversationApplicationService) ListConversation(ctx context.Context, req *conversation.ListConversationsApiRequest) (*conversation.ListConversationsApiResponse, error) {
|
|
|
|
resp := new(conversation.ListConversationsApiResponse)
|
|
|
|
apiKeyInfo := ctxutil.GetApiAuthFromCtx(ctx)
|
|
userID := apiKeyInfo.UserID
|
|
connectorID := apiKeyInfo.ConnectorID
|
|
|
|
if userID == 0 {
|
|
return resp, errorx.New(errno.ErrConversationNotFound)
|
|
}
|
|
if ptr.From(req.ConnectorID) == consts.WebSDKConnectorID {
|
|
connectorID = ptr.From(req.ConnectorID)
|
|
}
|
|
|
|
conversationDOList, hasMore, err := c.ConversationDomainSVC.List(ctx, &entity.ListMeta{
|
|
UserID: userID,
|
|
AgentID: req.GetBotID(),
|
|
ConnectorID: connectorID,
|
|
Scene: common.Scene_SceneOpenApi,
|
|
Page: int(req.GetPageNum()),
|
|
Limit: int(req.GetPageSize()),
|
|
OrderBy: func() *string {
|
|
if strings.ToLower(req.GetSortOrder()) == "asc" {
|
|
return ptr.Of("asc")
|
|
}
|
|
return nil
|
|
}(),
|
|
})
|
|
if err != nil {
|
|
return resp, err
|
|
}
|
|
conversationData := slices.Transform(conversationDOList, func(conv *entity.Conversation) *conversation.ConversationData {
|
|
return &conversation.ConversationData{
|
|
Id: conv.ID,
|
|
LastSectionID: &conv.SectionID,
|
|
ConnectorID: &conv.ConnectorID,
|
|
CreatedAt: conv.CreatedAt / 1000,
|
|
Name: ptr.Of(conv.Name),
|
|
MetaData: parseExt(conv.Ext),
|
|
}
|
|
})
|
|
|
|
resp.Data = &conversation.ListConversationData{
|
|
Conversations: conversationData,
|
|
HasMore: hasMore,
|
|
}
|
|
return resp, nil
|
|
}
|
|
|
|
func (c *ConversationApplicationService) DeleteConversation(ctx context.Context, req *conversation.DeleteConversationApiRequest) (*conversation.DeleteConversationApiResponse, error) {
|
|
resp := new(conversation.DeleteConversationApiResponse)
|
|
convID := req.GetConversationID()
|
|
|
|
apiKeyInfo := ctxutil.GetApiAuthFromCtx(ctx)
|
|
userID := apiKeyInfo.UserID
|
|
|
|
if userID == 0 {
|
|
return resp, errorx.New(errno.ErrConversationPermissionCode, errorx.KV("msg", "permission check failed"))
|
|
}
|
|
|
|
conversationDO, err := c.ConversationDomainSVC.GetByID(ctx, convID)
|
|
if err != nil {
|
|
return resp, err
|
|
}
|
|
if conversationDO == nil {
|
|
return resp, errorx.New(errno.ErrConversationNotFound)
|
|
}
|
|
if conversationDO.CreatorID != userID {
|
|
return resp, errorx.New(errno.ErrConversationNotFound, errorx.KV("msg", "user not match"))
|
|
}
|
|
err = c.ConversationDomainSVC.Delete(ctx, convID)
|
|
if err != nil {
|
|
return resp, err
|
|
}
|
|
return resp, nil
|
|
}
|
|
|
|
func (c *ConversationApplicationService) UpdateConversation(ctx context.Context, req *conversation.UpdateConversationApiRequest) (*conversation.UpdateConversationApiResponse, error) {
|
|
resp := new(conversation.UpdateConversationApiResponse)
|
|
convID := req.GetConversationID()
|
|
|
|
apiKeyInfo := ctxutil.GetApiAuthFromCtx(ctx)
|
|
userID := apiKeyInfo.UserID
|
|
|
|
if userID == 0 {
|
|
return resp, errorx.New(errno.ErrConversationPermissionCode, errorx.KV("msg", "permission check failed"))
|
|
}
|
|
|
|
conversationDO, err := c.ConversationDomainSVC.GetByID(ctx, convID)
|
|
if err != nil {
|
|
return resp, err
|
|
}
|
|
if conversationDO == nil {
|
|
return resp, errorx.New(errno.ErrConversationNotFound)
|
|
}
|
|
if conversationDO.CreatorID != userID {
|
|
return resp, errorx.New(errno.ErrConversationPermissionCode, errorx.KV("msg", "user not match"))
|
|
}
|
|
|
|
updateResult, err := c.ConversationDomainSVC.Update(ctx, &entity.UpdateMeta{
|
|
ID: convID,
|
|
Name: req.GetName(),
|
|
})
|
|
if err != nil {
|
|
return resp, err
|
|
}
|
|
resp.ConversationData = &conversation.ConversationData{
|
|
Id: updateResult.ID,
|
|
LastSectionID: &updateResult.SectionID,
|
|
ConnectorID: &updateResult.ConnectorID,
|
|
CreatedAt: updateResult.CreatedAt / 1000,
|
|
Name: ptr.Of(updateResult.Name),
|
|
}
|
|
return resp, nil
|
|
}
|
|
|