feat: Support for Chat Flow & Agent Support for binding a single chat flow (#765)
Co-authored-by: Yu Yang <72337138+tomasyu985@users.noreply.github.com> Co-authored-by: zengxiaohui <csu.zengxiaohui@gmail.com> Co-authored-by: lijunwen.gigoo <lijunwen.gigoo@bytedance.com> Co-authored-by: lvxinyu.1117 <lvxinyu.1117@bytedance.com> Co-authored-by: liuyunchao.0510 <liuyunchao.0510@bytedance.com> Co-authored-by: haozhenfei <37089575+haozhenfei@users.noreply.github.com> Co-authored-by: July <jiangxujin@bytedance.com> Co-authored-by: tecvan-fe <fanwenjie.fe@bytedance.com>
This commit is contained in:
@@ -27,6 +27,7 @@ import (
|
||||
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"
|
||||
@@ -48,6 +49,7 @@ var ConversationSVC = new(ConversationApplicationService)
|
||||
|
||||
type OpenapiAgentRunApplication struct {
|
||||
ShortcutDomainSVC service.ShortcutCmd
|
||||
UploaodDomainSVC uploadService.UploadService
|
||||
}
|
||||
|
||||
var ConversationOpenAPISVC = new(OpenapiAgentRunApplication)
|
||||
@@ -177,6 +179,7 @@ func (c *ConversationApplicationService) ListConversation(ctx context.Context, r
|
||||
LastSectionID: &conv.SectionID,
|
||||
ConnectorID: &conv.ConnectorID,
|
||||
CreatedAt: conv.CreatedAt / 1000,
|
||||
Name: ptr.Of(conv.Name),
|
||||
}
|
||||
})
|
||||
|
||||
@@ -186,3 +189,70 @@ func (c *ConversationApplicationService) ListConversation(ctx context.Context, r
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
message "github.com/coze-dev/coze-studio/backend/domain/conversation/message/service"
|
||||
shortcutRepo "github.com/coze-dev/coze-studio/backend/domain/shortcutcmd/repository"
|
||||
"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/infra/contract/idgen"
|
||||
"github.com/coze-dev/coze-studio/backend/infra/contract/imagex"
|
||||
"github.com/coze-dev/coze-studio/backend/infra/contract/storage"
|
||||
@@ -56,6 +57,7 @@ func InitService(s *ServiceComponents) *ConversationApplicationService {
|
||||
|
||||
arDomainComponents := &agentrun.Components{
|
||||
RunRecordRepo: repository.NewRunRecordRepo(s.DB, s.IDGen),
|
||||
ImagexSVC: s.ImageX,
|
||||
}
|
||||
|
||||
agentRunDomainSVC := agentrun.NewService(arDomainComponents)
|
||||
@@ -71,6 +73,9 @@ func InitService(s *ServiceComponents) *ConversationApplicationService {
|
||||
ConversationSVC.ShortcutDomainSVC = shortcutCmdDomainSVC
|
||||
|
||||
ConversationOpenAPISVC.ShortcutDomainSVC = shortcutCmdDomainSVC
|
||||
uploadSVC := uploadService.NewUploadSVC(s.DB, s.IDGen, s.TosClient)
|
||||
ConversationOpenAPISVC.UploaodDomainSVC = uploadSVC
|
||||
OpenapiMessageSVC.UploaodDomainSVC = uploadSVC
|
||||
|
||||
return ConversationSVC
|
||||
}
|
||||
|
||||
@@ -35,7 +35,9 @@ import (
|
||||
"github.com/coze-dev/coze-studio/backend/domain/conversation/agentrun/entity"
|
||||
convEntity "github.com/coze-dev/coze-studio/backend/domain/conversation/conversation/entity"
|
||||
cmdEntity "github.com/coze-dev/coze-studio/backend/domain/shortcutcmd/entity"
|
||||
uploadService "github.com/coze-dev/coze-studio/backend/domain/upload/service"
|
||||
sseImpl "github.com/coze-dev/coze-studio/backend/infra/impl/sse"
|
||||
"github.com/coze-dev/coze-studio/backend/pkg/errorx"
|
||||
"github.com/coze-dev/coze-studio/backend/pkg/lang/conv"
|
||||
"github.com/coze-dev/coze-studio/backend/pkg/lang/ptr"
|
||||
"github.com/coze-dev/coze-studio/backend/pkg/logs"
|
||||
@@ -237,11 +239,26 @@ func (a *OpenapiAgentRunApplication) buildMultiContent(ctx context.Context, ar *
|
||||
Text: ptr.From(one.Text),
|
||||
})
|
||||
case message.InputTypeImage, message.InputTypeFile:
|
||||
|
||||
var fileUrl, fileURI string
|
||||
if one.GetFileURL() != "" {
|
||||
fileUrl = one.GetFileURL()
|
||||
} else if one.GetFileID() != 0 {
|
||||
fileInfo, err := a.UploaodDomainSVC.GetFile(ctx, &uploadService.GetFileRequest{
|
||||
ID: one.GetFileID(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, contentType, err
|
||||
}
|
||||
fileUrl = fileInfo.File.Url
|
||||
fileURI = fileInfo.File.TosURI
|
||||
}
|
||||
multiContents = append(multiContents, &message.InputMetaData{
|
||||
Type: message.InputType(one.Type),
|
||||
FileData: []*message.FileData{
|
||||
{
|
||||
Url: one.GetFileURL(),
|
||||
Url: fileUrl,
|
||||
URI: fileURI,
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -328,3 +345,62 @@ func buildARSM2ApiChatMessage(chunk *entity.AgentRunResponse) []byte {
|
||||
mCM, _ := json.Marshal(chunkMessage)
|
||||
return mCM
|
||||
}
|
||||
|
||||
func (a *OpenapiAgentRunApplication) CancelRun(ctx context.Context, req *run.CancelChatApiRequest) (*run.CancelChatApiResponse, error) {
|
||||
resp := new(run.CancelChatApiResponse)
|
||||
|
||||
apiKeyInfo := ctxutil.GetApiAuthFromCtx(ctx)
|
||||
userID := apiKeyInfo.UserID
|
||||
|
||||
runRecord, err := ConversationSVC.AgentRunDomainSVC.GetByID(ctx, req.ChatID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if runRecord == nil {
|
||||
return nil, errorx.New(errno.ErrRecordNotFound)
|
||||
}
|
||||
|
||||
conversationData, err := ConversationSVC.ConversationDomainSVC.GetByID(ctx, req.ConversationID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if userID != conversationData.CreatorID {
|
||||
return nil, errorx.New(errno.ErrConversationPermissionCode, errorx.KV("msg", "user not match"))
|
||||
}
|
||||
|
||||
if runRecord.Status != entity.RunStatusInProgress && runRecord.Status != entity.RunStatusCreated {
|
||||
return nil, errorx.New(errno.ErrInProgressCanNotCancel)
|
||||
}
|
||||
|
||||
runMeta, err := ConversationSVC.AgentRunDomainSVC.Cancel(ctx, &entity.CancelRunMeta{
|
||||
RunID: req.ChatID,
|
||||
ConversationID: req.ConversationID,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if runMeta == nil {
|
||||
return nil, errorx.New(errno.ErrConversationAgentRunError)
|
||||
}
|
||||
|
||||
resp.ChatV3ChatDetail = &run.ChatV3ChatDetail{
|
||||
ID: runMeta.ID,
|
||||
ConversationID: runMeta.ConversationID,
|
||||
BotID: runMeta.AgentID,
|
||||
Status: string(runMeta.Status),
|
||||
SectionID: ptr.Of(runMeta.SectionID),
|
||||
CreatedAt: ptr.Of(int32(runMeta.CreatedAt / 1000)),
|
||||
CompletedAt: ptr.Of(int32(runMeta.CompletedAt / 1000)),
|
||||
FailedAt: ptr.Of(int32(runMeta.FailedAt / 1000)),
|
||||
}
|
||||
if runMeta.Usage != nil {
|
||||
resp.ChatV3ChatDetail.Usage = &run.Usage{
|
||||
TokenCount: ptr.Of(int32(runMeta.Usage.LlmTotalTokens)),
|
||||
InputTokens: ptr.Of(int32(runMeta.Usage.LlmPromptTokens)),
|
||||
OutputTokens: ptr.Of(int32(runMeta.Usage.LlmCompletionTokens)),
|
||||
}
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
@@ -18,23 +18,28 @@ 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{}
|
||||
type OpenapiMessageApplication struct {
|
||||
UploaodDomainSVC uploadService.UploadService
|
||||
}
|
||||
|
||||
var OpenapiMessageApplicationService = new(OpenapiMessageApplication)
|
||||
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
|
||||
@@ -61,13 +66,21 @@ func (m *OpenapiMessageApplication) GetApiMessageList(ctx context.Context, mr *m
|
||||
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.ScrollPageDirectionPrev
|
||||
msgListMeta.Direction = entity.ScrollPageDirectionNext
|
||||
msgListMeta.Cursor = *mr.BeforeID
|
||||
} else {
|
||||
msgListMeta.Direction = entity.ScrollPageDirectionNext
|
||||
msgListMeta.Direction = entity.ScrollPageDirectionPrev
|
||||
msgListMeta.Cursor = ptr.From(mr.AfterID)
|
||||
}
|
||||
if mr.Order == nil {
|
||||
@@ -76,20 +89,14 @@ func (m *OpenapiMessageApplication) GetApiMessageList(ctx context.Context, mr *m
|
||||
msgListMeta.OrderBy = mr.Order
|
||||
}
|
||||
|
||||
mListMessages, err := ConversationSVC.MessageDomainSVC.List(ctx, msgListMeta)
|
||||
mListMessages, err := ConversationSVC.MessageDomainSVC.ListWithoutPair(ctx, msgListMeta)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// get agent id
|
||||
var agentIDs []int64
|
||||
for _, mOne := range mListMessages.Messages {
|
||||
agentIDs = append(agentIDs, mOne.AgentID)
|
||||
}
|
||||
|
||||
resp := m.buildMessageListResponse(ctx, mListMessages, currentConversation)
|
||||
|
||||
return resp, err
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func getConversation(ctx context.Context, conversationID int64) (*convEntity.Conversation, error) {
|
||||
@@ -106,21 +113,22 @@ func (m *OpenapiMessageApplication) buildMessageListResponse(ctx context.Context
|
||||
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,
|
||||
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 = dm.DisplayContent
|
||||
msg.Content = m.parseDisplayContent(ctx, dm)
|
||||
msg.ContentType = run.ContentTypeMixApi
|
||||
}
|
||||
return msg
|
||||
@@ -135,3 +143,38 @@ func (m *OpenapiMessageApplication) buildMessageListResponse(ctx context.Context
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user