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:
@@ -18,17 +18,13 @@ package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/cloudwego/eino/schema"
|
||||
|
||||
"github.com/coze-dev/coze-studio/backend/api/model/crossdomain/agentrun"
|
||||
"github.com/coze-dev/coze-studio/backend/api/model/crossdomain/message"
|
||||
model "github.com/coze-dev/coze-studio/backend/api/model/crossdomain/singleagent"
|
||||
crossagent "github.com/coze-dev/coze-studio/backend/crossdomain/contract/agent"
|
||||
singleagent "github.com/coze-dev/coze-studio/backend/domain/agent/singleagent/service"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/conversation/message/entity"
|
||||
"github.com/coze-dev/coze-studio/backend/infra/contract/imagex"
|
||||
"github.com/coze-dev/coze-studio/backend/pkg/lang/conv"
|
||||
"github.com/coze-dev/coze-studio/backend/pkg/lang/slices"
|
||||
"github.com/coze-dev/coze-studio/backend/pkg/logs"
|
||||
@@ -38,49 +34,34 @@ var defaultSVC crossagent.SingleAgent
|
||||
|
||||
type impl struct {
|
||||
DomainSVC singleagent.SingleAgent
|
||||
ImagexSVC imagex.ImageX
|
||||
}
|
||||
|
||||
func InitDomainService(c singleagent.SingleAgent, imagexClient imagex.ImageX) crossagent.SingleAgent {
|
||||
func InitDomainService(c singleagent.SingleAgent) crossagent.SingleAgent {
|
||||
defaultSVC = &impl{
|
||||
DomainSVC: c,
|
||||
ImagexSVC: imagexClient,
|
||||
}
|
||||
|
||||
return defaultSVC
|
||||
}
|
||||
|
||||
func (c *impl) StreamExecute(ctx context.Context, historyMsg []*message.Message,
|
||||
query *message.Message, agentRuntime *model.AgentRuntime,
|
||||
func (c *impl) StreamExecute(ctx context.Context, agentRuntime *crossagent.AgentRuntime,
|
||||
) (*schema.StreamReader[*model.AgentEvent], error) {
|
||||
|
||||
historyMsg = c.historyPairs(historyMsg)
|
||||
|
||||
singleAgentStreamExecReq := c.buildSingleAgentStreamExecuteReq(ctx, historyMsg, query, agentRuntime)
|
||||
singleAgentStreamExecReq := c.buildSingleAgentStreamExecuteReq(ctx, agentRuntime)
|
||||
|
||||
streamEvent, err := c.DomainSVC.StreamExecute(ctx, singleAgentStreamExecReq)
|
||||
logs.CtxInfof(ctx, "agent StreamExecute req:%v, streamEvent:%v, err:%v", conv.DebugJsonToStr(singleAgentStreamExecReq), streamEvent, err)
|
||||
return streamEvent, err
|
||||
}
|
||||
|
||||
func (c *impl) buildSingleAgentStreamExecuteReq(ctx context.Context, historyMsg []*message.Message,
|
||||
input *message.Message, agentRuntime *model.AgentRuntime,
|
||||
func (c *impl) buildSingleAgentStreamExecuteReq(ctx context.Context, agentRuntime *crossagent.AgentRuntime,
|
||||
) *model.ExecuteRequest {
|
||||
identity := c.buildIdentity(input, agentRuntime)
|
||||
inputBuild := c.buildSchemaMessage(ctx, []*message.Message{input})
|
||||
var inputSM *schema.Message
|
||||
if len(inputBuild) > 0 {
|
||||
inputSM = inputBuild[0]
|
||||
}
|
||||
history := c.buildSchemaMessage(ctx, historyMsg)
|
||||
|
||||
resumeInfo := c.checkResumeInfo(ctx, historyMsg)
|
||||
|
||||
return &model.ExecuteRequest{
|
||||
Identity: identity,
|
||||
Input: inputSM,
|
||||
History: history,
|
||||
UserID: input.UserID,
|
||||
Identity: c.buildIdentity(agentRuntime),
|
||||
Input: agentRuntime.Input,
|
||||
History: agentRuntime.HistoryMsg,
|
||||
UserID: agentRuntime.UserID,
|
||||
PreCallTools: slices.Transform(agentRuntime.PreRetrieveTools, func(tool *agentrun.Tool) *agentrun.ToolsRetriever {
|
||||
return &agentrun.ToolsRetriever{
|
||||
PluginID: tool.PluginID,
|
||||
@@ -98,141 +79,19 @@ func (c *impl) buildSingleAgentStreamExecuteReq(ctx context.Context, historyMsg
|
||||
}(tool.Type),
|
||||
}
|
||||
}),
|
||||
|
||||
ResumeInfo: resumeInfo,
|
||||
ResumeInfo: agentRuntime.ResumeInfo,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *impl) historyPairs(historyMsg []*message.Message) []*message.Message {
|
||||
|
||||
fcMsgPairs := make(map[int64][]*message.Message)
|
||||
for _, one := range historyMsg {
|
||||
if one.MessageType != message.MessageTypeFunctionCall && one.MessageType != message.MessageTypeToolResponse {
|
||||
continue
|
||||
}
|
||||
if _, ok := fcMsgPairs[one.RunID]; !ok {
|
||||
fcMsgPairs[one.RunID] = []*message.Message{one}
|
||||
} else {
|
||||
fcMsgPairs[one.RunID] = append(fcMsgPairs[one.RunID], one)
|
||||
}
|
||||
}
|
||||
|
||||
var historyAfterPairs []*message.Message
|
||||
for _, value := range historyMsg {
|
||||
if value.MessageType == message.MessageTypeFunctionCall {
|
||||
if len(fcMsgPairs[value.RunID])%2 == 0 {
|
||||
historyAfterPairs = append(historyAfterPairs, value)
|
||||
}
|
||||
} else {
|
||||
historyAfterPairs = append(historyAfterPairs, value)
|
||||
}
|
||||
}
|
||||
return historyAfterPairs
|
||||
|
||||
}
|
||||
func (c *impl) checkResumeInfo(_ context.Context, historyMsg []*message.Message) *crossagent.ResumeInfo {
|
||||
|
||||
var resumeInfo *crossagent.ResumeInfo
|
||||
for i := len(historyMsg) - 1; i >= 0; i-- {
|
||||
if historyMsg[i].MessageType == message.MessageTypeQuestion {
|
||||
break
|
||||
}
|
||||
if historyMsg[i].MessageType == message.MessageTypeVerbose {
|
||||
if historyMsg[i].Ext[string(entity.ExtKeyResumeInfo)] != "" {
|
||||
err := json.Unmarshal([]byte(historyMsg[i].Ext[string(entity.ExtKeyResumeInfo)]), &resumeInfo)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return resumeInfo
|
||||
}
|
||||
|
||||
func (c *impl) buildSchemaMessage(ctx context.Context, msgs []*message.Message) []*schema.Message {
|
||||
schemaMessage := make([]*schema.Message, 0, len(msgs))
|
||||
|
||||
for _, msgOne := range msgs {
|
||||
if msgOne.ModelContent == "" {
|
||||
continue
|
||||
}
|
||||
if msgOne.MessageType == message.MessageTypeVerbose || msgOne.MessageType == message.MessageTypeFlowUp {
|
||||
continue
|
||||
}
|
||||
var sm *schema.Message
|
||||
err := json.Unmarshal([]byte(msgOne.ModelContent), &sm)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if len(sm.ReasoningContent) > 0 {
|
||||
sm.ReasoningContent = ""
|
||||
}
|
||||
|
||||
schemaMessage = append(schemaMessage, c.parseMessageURI(ctx, sm))
|
||||
}
|
||||
|
||||
return schemaMessage
|
||||
}
|
||||
|
||||
func (c *impl) parseMessageURI(ctx context.Context, mcMsg *schema.Message) *schema.Message {
|
||||
if mcMsg.MultiContent == nil {
|
||||
return mcMsg
|
||||
}
|
||||
for k, one := range mcMsg.MultiContent {
|
||||
switch one.Type {
|
||||
case schema.ChatMessagePartTypeImageURL:
|
||||
|
||||
if one.ImageURL.URI != "" {
|
||||
url, err := c.ImagexSVC.GetResourceURL(ctx, one.ImageURL.URI)
|
||||
if err == nil {
|
||||
mcMsg.MultiContent[k].ImageURL.URL = url.URL
|
||||
}
|
||||
}
|
||||
case schema.ChatMessagePartTypeFileURL:
|
||||
if one.FileURL.URI != "" {
|
||||
url, err := c.ImagexSVC.GetResourceURL(ctx, one.FileURL.URI)
|
||||
if err == nil {
|
||||
mcMsg.MultiContent[k].FileURL.URL = url.URL
|
||||
}
|
||||
}
|
||||
case schema.ChatMessagePartTypeAudioURL:
|
||||
if one.AudioURL.URI != "" {
|
||||
url, err := c.ImagexSVC.GetResourceURL(ctx, one.AudioURL.URI)
|
||||
if err == nil {
|
||||
mcMsg.MultiContent[k].AudioURL.URL = url.URL
|
||||
}
|
||||
}
|
||||
case schema.ChatMessagePartTypeVideoURL:
|
||||
if one.VideoURL.URI != "" {
|
||||
url, err := c.ImagexSVC.GetResourceURL(ctx, one.VideoURL.URI)
|
||||
if err == nil {
|
||||
mcMsg.MultiContent[k].VideoURL.URL = url.URL
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return mcMsg
|
||||
}
|
||||
|
||||
func (c *impl) buildIdentity(input *message.Message, agentRuntime *model.AgentRuntime) *model.AgentIdentity {
|
||||
func (c *impl) buildIdentity(agentRuntime *crossagent.AgentRuntime) *model.AgentIdentity {
|
||||
return &model.AgentIdentity{
|
||||
AgentID: input.AgentID,
|
||||
AgentID: agentRuntime.AgentID,
|
||||
Version: agentRuntime.AgentVersion,
|
||||
IsDraft: agentRuntime.IsDraft,
|
||||
ConnectorID: agentRuntime.ConnectorID,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *impl) GetSingleAgent(ctx context.Context, agentID int64, version string) (agent *model.SingleAgent, err error) {
|
||||
agentInfo, err := c.DomainSVC.GetSingleAgent(ctx, agentID, version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return agentInfo.SingleAgent, nil
|
||||
}
|
||||
|
||||
func (c *impl) ObtainAgentByIdentity(ctx context.Context, identity *model.AgentIdentity) (*model.SingleAgent, error) {
|
||||
agentInfo, err := c.DomainSVC.ObtainAgentByIdentity(ctx, identity)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user