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:
Zhj
2025-08-28 21:53:32 +08:00
committed by GitHub
parent bbc615a18e
commit d70101c979
503 changed files with 48036 additions and 3427 deletions

View File

@ -26,6 +26,8 @@ import (
"github.com/cloudwego/eino/schema"
workflowModel "github.com/coze-dev/coze-studio/backend/api/model/crossdomain/workflow"
workflowapimodel "github.com/coze-dev/coze-studio/backend/api/model/workflow"
crossmessage "github.com/coze-dev/coze-studio/backend/crossdomain/contract/message"
"github.com/coze-dev/coze-studio/backend/domain/workflow"
"github.com/coze-dev/coze-studio/backend/domain/workflow/entity"
"github.com/coze-dev/coze-studio/backend/domain/workflow/entity/vo"
@ -62,6 +64,8 @@ func (i *impl) SyncExecute(ctx context.Context, config workflowModel.ExecuteConf
return nil, "", err
}
config.WorkflowMode = wfEntity.Mode
isApplicationWorkflow := wfEntity.AppID != nil
if isApplicationWorkflow && config.Mode == workflowModel.ExecuteModeRelease {
err = i.checkApplicationWorkflowReleaseVersion(ctx, *wfEntity.AppID, config.ConnectorID, config.ID, config.Version)
@ -207,6 +211,8 @@ func (i *impl) AsyncExecute(ctx context.Context, config workflowModel.ExecuteCon
return 0, err
}
config.WorkflowMode = wfEntity.Mode
isApplicationWorkflow := wfEntity.AppID != nil
if isApplicationWorkflow && config.Mode == workflowModel.ExecuteModeRelease {
err = i.checkApplicationWorkflowReleaseVersion(ctx, *wfEntity.AppID, config.ConnectorID, config.ID, config.Version)
@ -292,6 +298,8 @@ func (i *impl) AsyncExecuteNode(ctx context.Context, nodeID string, config workf
return 0, err
}
config.WorkflowMode = wfEntity.Mode
isApplicationWorkflow := wfEntity.AppID != nil
if isApplicationWorkflow && config.Mode == workflowModel.ExecuteModeRelease {
err = i.checkApplicationWorkflowReleaseVersion(ctx, *wfEntity.AppID, config.ConnectorID, config.ID, config.Version)
@ -300,6 +308,30 @@ func (i *impl) AsyncExecuteNode(ctx context.Context, nodeID string, config workf
}
}
historyRounds := int64(0)
if config.WorkflowMode == workflowapimodel.WorkflowMode_ChatFlow {
historyRounds, err = getHistoryRoundsFromNode(ctx, wfEntity, nodeID, i.repo)
if err != nil {
return 0, err
}
}
if historyRounds > 0 {
messages, scMessages, err := i.prefetchChatHistory(ctx, config, historyRounds)
if err != nil {
logs.CtxErrorf(ctx, "failed to prefetch chat history: %v", err)
}
if len(messages) > 0 {
config.ConversationHistory = messages
}
if len(scMessages) > 0 {
config.ConversationHistorySchemaMessages = scMessages
}
}
c := &vo.Canvas{}
if err = sonic.UnmarshalString(wfEntity.Canvas, c); err != nil {
return 0, fmt.Errorf("failed to unmarshal canvas: %w", err)
@ -375,6 +407,8 @@ func (i *impl) StreamExecute(ctx context.Context, config workflowModel.ExecuteCo
return nil, err
}
config.WorkflowMode = wfEntity.Mode
isApplicationWorkflow := wfEntity.AppID != nil
if isApplicationWorkflow && config.Mode == workflowModel.ExecuteModeRelease {
err = i.checkApplicationWorkflowReleaseVersion(ctx, *wfEntity.AppID, config.ConnectorID, config.ID, config.Version)
@ -383,6 +417,29 @@ func (i *impl) StreamExecute(ctx context.Context, config workflowModel.ExecuteCo
}
}
historyRounds := int64(0)
if config.WorkflowMode == workflowapimodel.WorkflowMode_ChatFlow {
historyRounds, err = i.calculateMaxChatHistoryRounds(ctx, wfEntity, i.repo)
if err != nil {
return nil, err
}
}
if historyRounds > 0 {
messages, scMessages, err := i.prefetchChatHistory(ctx, config, historyRounds)
if err != nil {
logs.CtxErrorf(ctx, "failed to prefetch chat history: %v", err)
}
if len(messages) > 0 {
config.ConversationHistory = messages
}
if len(scMessages) > 0 {
config.ConversationHistorySchemaMessages = scMessages
}
}
c := &vo.Canvas{}
if err = sonic.UnmarshalString(wfEntity.Canvas, c); err != nil {
return nil, fmt.Errorf("failed to unmarshal canvas: %w", err)
@ -718,6 +775,7 @@ func (i *impl) AsyncResume(ctx context.Context, req *entity.ResumeRequest, confi
config.AppID = wfExe.AppID
config.AgentID = wfExe.AgentID
config.CommitID = wfExe.CommitID
config.WorkflowMode = wfEntity.Mode
if config.ConnectorID == 0 {
config.ConnectorID = wfExe.ConnectorID
@ -859,6 +917,7 @@ func (i *impl) StreamResume(ctx context.Context, req *entity.ResumeRequest, conf
config.AppID = wfExe.AppID
config.AgentID = wfExe.AgentID
config.CommitID = wfExe.CommitID
config.WorkflowMode = wfEntity.Mode
if config.ConnectorID == 0 {
config.ConnectorID = wfExe.ConnectorID
@ -937,3 +996,73 @@ func (i *impl) checkApplicationWorkflowReleaseVersion(ctx context.Context, appID
return nil
}
const maxHistoryRounds int64 = 30
func (i *impl) calculateMaxChatHistoryRounds(ctx context.Context, wfEntity *entity.Workflow, repo workflow.Repository) (int64, error) {
if wfEntity == nil {
return 0, nil
}
maxRounds, err := getMaxHistoryRoundsRecursively(ctx, wfEntity, repo)
if err != nil {
return 0, err
}
return min(maxRounds, maxHistoryRounds), nil
}
func (i *impl) prefetchChatHistory(ctx context.Context, config workflowModel.ExecuteConfig, historyRounds int64) ([]*crossmessage.WfMessage, []*schema.Message, error) {
convID := config.ConversationID
agentID := config.AgentID
appID := config.AppID
userID := config.Operator
sectionID := config.SectionID
if sectionID == nil {
logs.CtxWarnf(ctx, "SectionID is nil, skipping chat history")
return nil, nil, nil
}
if convID == nil || *convID == 0 {
logs.CtxWarnf(ctx, "ConversationID is 0 or nil, skipping chat history")
return nil, nil, nil
}
var resolvedAppID int64
if appID != nil {
resolvedAppID = *appID
} else if agentID != nil {
resolvedAppID = *agentID
} else {
logs.CtxWarnf(ctx, "AppID and AgentID are both nil, skipping chat history")
return nil, nil, nil
}
runIdsReq := &crossmessage.GetLatestRunIDsRequest{
ConversationID: *convID,
AppID: resolvedAppID,
UserID: userID,
Rounds: historyRounds + 1,
SectionID: *sectionID,
}
runIds, err := crossmessage.DefaultSVC().GetLatestRunIDs(ctx, runIdsReq)
if err != nil {
logs.CtxErrorf(ctx, "failed to get latest run ids: %v", err)
return nil, nil, nil
}
if len(runIds) <= 1 {
return []*crossmessage.WfMessage{}, []*schema.Message{}, nil
}
runIds = runIds[1:]
response, err := crossmessage.DefaultSVC().GetMessagesByRunIDs(ctx, &crossmessage.GetMessagesByRunIDsRequest{
ConversationID: *convID,
RunIDs: runIds,
})
if err != nil {
logs.CtxErrorf(ctx, "failed to get messages by run ids: %v", err)
return nil, nil, nil
}
return response.Messages, response.SchemaMessages, nil
}