feat: manually mirror opencoze's code from bytedance
Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
85
backend/domain/conversation/agentrun/entity/const.go
Normal file
85
backend/domain/conversation/agentrun/entity/const.go
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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 entity
|
||||
|
||||
const ConversationTurnsDefault int32 = 100
|
||||
|
||||
type RunStatus string
|
||||
|
||||
const (
|
||||
RunStatusCreated RunStatus = "created"
|
||||
RunStatusInProgress RunStatus = "in_progress"
|
||||
RunStatusCompleted RunStatus = "completed"
|
||||
RunStatusFailed RunStatus = "failed"
|
||||
RunStatusExpired RunStatus = "expired"
|
||||
RunStatusCancelled RunStatus = "cancelled"
|
||||
RunStatusRequiredAction RunStatus = "required_action"
|
||||
RunStatusDeleted RunStatus = "deleted"
|
||||
)
|
||||
|
||||
type RunEvent string
|
||||
|
||||
const (
|
||||
RunEventCreated RunEvent = "conversation.run.created"
|
||||
RunEventInProgress RunEvent = "conversation.run.in_progress"
|
||||
RunEventCompleted RunEvent = "conversation.run.completed"
|
||||
RunEventFailed RunEvent = "conversation.run.failed"
|
||||
RunEventExpired RunEvent = "conversation.run.expired"
|
||||
RunEventCancelled RunEvent = "conversation.run.cancelled"
|
||||
RunEventRequiredAction RunEvent = "conversation.run.required_action"
|
||||
|
||||
RunEventMessageDelta RunEvent = "conversation.message.delta"
|
||||
RunEventMessageCompleted RunEvent = "conversation.message.completed"
|
||||
|
||||
RunEventAck = "conversation.ack"
|
||||
RunEventError RunEvent = "conversation.error"
|
||||
RunEventStreamDone RunEvent = "conversation.stream.done"
|
||||
)
|
||||
|
||||
type ReplyType int64
|
||||
|
||||
const (
|
||||
ReplyTypeAnswer ReplyType = 1
|
||||
ReplyTypeSuggest ReplyType = 2
|
||||
ReplyTypeLLMOutput ReplyType = 3
|
||||
ReplyTypeToolOutput ReplyType = 4
|
||||
ReplyTypeVerbose ReplyType = 100
|
||||
ReplyTypePlaceHolder ReplyType = 101
|
||||
)
|
||||
|
||||
type MetaType int64
|
||||
|
||||
const (
|
||||
MetaTypeKnowledgeCard MetaType = 4
|
||||
)
|
||||
|
||||
type RoleType string
|
||||
|
||||
const (
|
||||
RoleTypeSystem RoleType = "system"
|
||||
RoleTypeUser RoleType = "user"
|
||||
RoleTypeAssistant RoleType = "assistant"
|
||||
RoleTypeTool RoleType = "tool"
|
||||
)
|
||||
|
||||
type MessageSubType string
|
||||
|
||||
const (
|
||||
MessageSubTypeKnowledgeCall MessageSubType = "knowledge_recall"
|
||||
MessageSubTypeGenerateFinish MessageSubType = "generate_answer_finish"
|
||||
MessageSubTypeInterrupt MessageSubType = "interrupt"
|
||||
)
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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 entity
|
||||
|
||||
type EventType int64
|
||||
|
||||
const (
|
||||
EventType_LocalPlugin EventType = 1
|
||||
EventType_Question EventType = 2
|
||||
EventType_RequireInfos EventType = 3
|
||||
EventType_SceneChat EventType = 4
|
||||
EventType_InputNode EventType = 5
|
||||
EventType_WorkflowLocalPlugin EventType = 6
|
||||
EventType_OauthPlugin EventType = 7
|
||||
EventType_WorkflowLLM EventType = 100
|
||||
)
|
||||
158
backend/domain/conversation/agentrun/entity/run_record.go
Normal file
158
backend/domain/conversation/agentrun/entity/run_record.go
Normal file
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* 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 entity
|
||||
|
||||
import (
|
||||
"github.com/cloudwego/eino/schema"
|
||||
|
||||
"github.com/coze-dev/coze-studio/backend/api/model/conversation/common"
|
||||
message2 "github.com/coze-dev/coze-studio/backend/api/model/conversation/message"
|
||||
"github.com/coze-dev/coze-studio/backend/api/model/crossdomain/agentrun"
|
||||
"github.com/coze-dev/coze-studio/backend/api/model/crossdomain/message"
|
||||
"github.com/coze-dev/coze-studio/backend/api/model/crossdomain/singleagent"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/conversation/agentrun/internal/dal/model"
|
||||
)
|
||||
|
||||
type RunRecord = model.RunRecord
|
||||
|
||||
type RunRecordMeta struct {
|
||||
ID int64 `json:"id"`
|
||||
ConversationID int64 `json:"conversation_id"`
|
||||
SectionID int64 `json:"section_id"`
|
||||
AgentID int64 `json:"agent_id"`
|
||||
Status RunStatus `json:"status"`
|
||||
Error *RunError `json:"error"`
|
||||
Usage *agentrun.Usage `json:"usage"`
|
||||
Ext string `json:"ext"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at"`
|
||||
ChatRequest *string `json:"chat_message"`
|
||||
CompletedAt int64 `json:"completed_at"`
|
||||
FailedAt int64 `json:"failed_at"`
|
||||
}
|
||||
|
||||
type ChunkRunItem = RunRecordMeta
|
||||
|
||||
type ChunkMessageItem struct {
|
||||
ID int64 `json:"id"`
|
||||
ConversationID int64 `json:"conversation_id"`
|
||||
SectionID int64 `json:"section_id"`
|
||||
RunID int64 `json:"run_id"`
|
||||
AgentID int64 `json:"agent_id"`
|
||||
Role RoleType `json:"role"`
|
||||
Type message.MessageType `json:"type"`
|
||||
Content string `json:"content"`
|
||||
ContentType message.ContentType `json:"content_type"`
|
||||
MessageType message.MessageType `json:"message_type"`
|
||||
ReplyID int64 `json:"reply_id"`
|
||||
Ext map[string]string `json:"ext"`
|
||||
ReasoningContent *string `json:"reasoning_content"`
|
||||
Index int64 `json:"index"`
|
||||
RequiredAction *message2.RequiredAction `json:"required_action"`
|
||||
SeqID int64 `json:"seq_id"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at"`
|
||||
IsFinish bool `json:"is_finish"`
|
||||
}
|
||||
|
||||
type RunError struct {
|
||||
Code int64 `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
}
|
||||
|
||||
type CustomerConfig struct {
|
||||
ModelConfig *ModelConfig `json:"model_config"`
|
||||
AgentConfig *AgentConfig `json:"agent_config"`
|
||||
}
|
||||
|
||||
type ModelConfig struct {
|
||||
ModelId *int64 `json:"model_id,omitempty"`
|
||||
}
|
||||
|
||||
type AgentConfig struct {
|
||||
Prompt *string `json:"prompt"`
|
||||
}
|
||||
|
||||
type Tool = agentrun.Tool
|
||||
|
||||
type AnswerFinshContent struct {
|
||||
MsgType MessageSubType `json:"msg_type"`
|
||||
Data string `json:"data"`
|
||||
FromUnit string `json:"from_unit"`
|
||||
}
|
||||
type Data struct {
|
||||
FinishReason int32 `json:"finish_reason"`
|
||||
FinData string `json:"fin_data"`
|
||||
}
|
||||
|
||||
type MetaInfo struct {
|
||||
Type MetaType `json:"type"`
|
||||
Info string `json:"info"`
|
||||
}
|
||||
|
||||
type AgentRunMeta struct {
|
||||
ConversationID int64 `json:"conversation_id"`
|
||||
ConnectorID int64 `json:"connector_id"`
|
||||
SpaceID int64 `json:"space_id"`
|
||||
Scene common.Scene `json:"scene"`
|
||||
SectionID int64 `json:"section_id"`
|
||||
Name string `json:"name"`
|
||||
UserID string `json:"user_id"`
|
||||
AgentID int64 `json:"agent_id"`
|
||||
ContentType message.ContentType `json:"content_type"`
|
||||
Content []*message.InputMetaData `json:"content"`
|
||||
PreRetrieveTools []*Tool `json:"tools"`
|
||||
IsDraft bool `json:"is_draft"`
|
||||
CustomerConfig *CustomerConfig `json:"customer_config"`
|
||||
DisplayContent string `json:"display_content"`
|
||||
CustomVariables map[string]string `json:"custom_variables"`
|
||||
Version string `json:"version"`
|
||||
Ext map[string]string `json:"ext"`
|
||||
}
|
||||
|
||||
type UpdateMeta struct {
|
||||
Status RunStatus
|
||||
LastError *RunError
|
||||
Usage *agentrun.Usage
|
||||
UpdatedAt int64
|
||||
CompletedAt int64
|
||||
FailedAt int64
|
||||
}
|
||||
|
||||
type AgentRunResponse struct {
|
||||
Event RunEvent `json:"event"`
|
||||
ChunkRunItem *ChunkRunItem `json:"run_record_item"`
|
||||
ChunkMessageItem *ChunkMessageItem `json:"message_item"`
|
||||
Error *RunError `json:"error"`
|
||||
}
|
||||
|
||||
type AgentRespEvent struct {
|
||||
EventType message.MessageType
|
||||
|
||||
ModelAnswer *schema.StreamReader[*schema.Message]
|
||||
ToolsMessage []*schema.Message
|
||||
FuncCall *schema.Message
|
||||
Suggest *schema.Message
|
||||
Knowledge []*schema.Document
|
||||
Interrupt *singleagent.InterruptInfo
|
||||
Err error
|
||||
}
|
||||
|
||||
type ModelAnswerEvent struct {
|
||||
Message *schema.Message
|
||||
Err error
|
||||
}
|
||||
167
backend/domain/conversation/agentrun/internal/dal/dao.go
Normal file
167
backend/domain/conversation/agentrun/internal/dal/dao.go
Normal file
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* 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 dal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/coze-dev/coze-studio/backend/domain/conversation/agentrun/entity"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/conversation/agentrun/internal/dal/model"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/conversation/agentrun/internal/dal/query"
|
||||
"github.com/coze-dev/coze-studio/backend/infra/contract/idgen"
|
||||
"github.com/coze-dev/coze-studio/backend/pkg/logs"
|
||||
)
|
||||
|
||||
type RunRecordDAO struct {
|
||||
db *gorm.DB
|
||||
query *query.Query
|
||||
idGen idgen.IDGenerator
|
||||
}
|
||||
|
||||
func NewRunRecordDAO(db *gorm.DB, idGen idgen.IDGenerator) *RunRecordDAO {
|
||||
return &RunRecordDAO{
|
||||
db: db,
|
||||
idGen: idGen,
|
||||
query: query.Use(db),
|
||||
}
|
||||
}
|
||||
|
||||
func (dao *RunRecordDAO) Create(ctx context.Context, runMeta *entity.AgentRunMeta) (*entity.RunRecordMeta, error) {
|
||||
|
||||
createPO, err := dao.buildCreatePO(ctx, runMeta)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
createErr := dao.query.RunRecord.WithContext(ctx).Create(createPO)
|
||||
if createErr != nil {
|
||||
return nil, createErr
|
||||
}
|
||||
|
||||
return dao.buildPo2Do(createPO), nil
|
||||
}
|
||||
|
||||
func (dao *RunRecordDAO) GetByID(ctx context.Context, id int64) (*model.RunRecord, error) {
|
||||
return dao.query.RunRecord.WithContext(ctx).Where(dao.query.RunRecord.ID.Eq(id)).First()
|
||||
}
|
||||
|
||||
func (dao *RunRecordDAO) UpdateByID(ctx context.Context, id int64, updateMeta *entity.UpdateMeta) error {
|
||||
po := &model.RunRecord{
|
||||
ID: id,
|
||||
}
|
||||
if updateMeta.Status != "" {
|
||||
|
||||
po.Status = string(updateMeta.Status)
|
||||
}
|
||||
if updateMeta.LastError != nil {
|
||||
errString, err := json.Marshal(updateMeta.LastError)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
po.LastError = string(errString)
|
||||
}
|
||||
if updateMeta.CompletedAt != 0 {
|
||||
|
||||
po.CompletedAt = updateMeta.CompletedAt
|
||||
}
|
||||
if updateMeta.FailedAt != 0 {
|
||||
|
||||
po.FailedAt = updateMeta.FailedAt
|
||||
}
|
||||
if updateMeta.Usage != nil {
|
||||
|
||||
po.Usage = updateMeta.Usage
|
||||
}
|
||||
po.UpdatedAt = time.Now().UnixMilli()
|
||||
|
||||
_, err := dao.query.RunRecord.WithContext(ctx).Where(dao.query.RunRecord.ID.Eq(id)).Updates(po)
|
||||
return err
|
||||
}
|
||||
|
||||
func (dao *RunRecordDAO) Delete(ctx context.Context, id []int64) error {
|
||||
|
||||
_, err := dao.query.RunRecord.WithContext(ctx).Where(dao.query.RunRecord.ID.In(id...)).UpdateColumns(map[string]interface{}{
|
||||
"updated_at": time.Now().UnixMilli(),
|
||||
"status": entity.RunStatusDeleted,
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (dao *RunRecordDAO) List(ctx context.Context, conversationID int64, sectionID int64, limit int32) ([]*model.RunRecord, error) {
|
||||
logs.CtxInfof(ctx, "list run record req:%v, sectionID:%v, limit:%v", conversationID, sectionID, limit)
|
||||
m := dao.query.RunRecord
|
||||
do := m.WithContext(ctx).Where(m.ConversationID.Eq(conversationID)).Debug().Where(m.Status.NotIn(string(entity.RunStatusDeleted)))
|
||||
|
||||
if sectionID > 0 {
|
||||
do = do.Where(m.SectionID.Eq(sectionID))
|
||||
}
|
||||
if limit > 0 {
|
||||
do = do.Limit(int(limit))
|
||||
}
|
||||
|
||||
runRecords, err := do.Order(m.CreatedAt.Desc()).Find()
|
||||
return runRecords, err
|
||||
}
|
||||
|
||||
func (dao *RunRecordDAO) buildCreatePO(ctx context.Context, runMeta *entity.AgentRunMeta) (*model.RunRecord, error) {
|
||||
|
||||
runID, err := dao.idGen.GenID(ctx)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reqOrigin, err := json.Marshal(runMeta)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
timeNow := time.Now().UnixMilli()
|
||||
|
||||
return &model.RunRecord{
|
||||
ID: runID,
|
||||
ConversationID: runMeta.ConversationID,
|
||||
SectionID: runMeta.SectionID,
|
||||
AgentID: runMeta.AgentID,
|
||||
Status: string(entity.RunStatusCreated),
|
||||
ChatRequest: string(reqOrigin),
|
||||
UserID: runMeta.UserID,
|
||||
CreatedAt: timeNow,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (dao *RunRecordDAO) buildPo2Do(po *model.RunRecord) *entity.RunRecordMeta {
|
||||
runMeta := &entity.RunRecordMeta{
|
||||
ID: po.ID,
|
||||
ConversationID: po.ConversationID,
|
||||
SectionID: po.SectionID,
|
||||
AgentID: po.AgentID,
|
||||
Status: entity.RunStatus(po.Status),
|
||||
Ext: po.Ext,
|
||||
CreatedAt: po.CreatedAt,
|
||||
UpdatedAt: po.UpdatedAt,
|
||||
CompletedAt: po.CompletedAt,
|
||||
FailedAt: po.FailedAt,
|
||||
Usage: po.Usage,
|
||||
}
|
||||
|
||||
return runMeta
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
|
||||
package model
|
||||
|
||||
import "github.com/coze-dev/coze-studio/backend/api/model/crossdomain/agentrun"
|
||||
|
||||
const TableNameRunRecord = "run_record"
|
||||
|
||||
// RunRecord 执行记录表
|
||||
type RunRecord struct {
|
||||
ID int64 `gorm:"column:id;primaryKey;comment:主键ID" json:"id"` // 主键ID
|
||||
ConversationID int64 `gorm:"column:conversation_id;not null;comment:会话 ID" json:"conversation_id"` // 会话 ID
|
||||
SectionID int64 `gorm:"column:section_id;not null;comment:section ID" json:"section_id"` // section ID
|
||||
AgentID int64 `gorm:"column:agent_id;not null;comment:agent_id" json:"agent_id"` // agent_id
|
||||
UserID string `gorm:"column:user_id;not null;comment:user id" json:"user_id"` // user id
|
||||
Source int32 `gorm:"column:source;not null;comment:执行来源 0 API," json:"source"` // 执行来源 0 API,
|
||||
Status string `gorm:"column:status;not null;comment:状态,0 Unknown, 1-Created,2-InProgress,3-Completed,4-Failed,5-Expired,6-Cancelled,7-RequiresAction" json:"status"` // 状态,0 Unknown, 1-Created,2-InProgress,3-Completed,4-Failed,5-Expired,6-Cancelled,7-RequiresAction
|
||||
CreatorID int64 `gorm:"column:creator_id;not null;comment:创建者标识" json:"creator_id"` // 创建者标识
|
||||
CreatedAt int64 `gorm:"column:created_at;not null;autoCreateTime:milli;comment:创建时间" json:"created_at"` // 创建时间
|
||||
UpdatedAt int64 `gorm:"column:updated_at;not null;autoUpdateTime:milli;comment:更新时间" json:"updated_at"` // 更新时间
|
||||
FailedAt int64 `gorm:"column:failed_at;not null;comment:失败时间" json:"failed_at"` // 失败时间
|
||||
LastError string `gorm:"column:last_error;comment:error message" json:"last_error"` // error message
|
||||
CompletedAt int64 `gorm:"column:completed_at;not null;comment:结束时间" json:"completed_at"` // 结束时间
|
||||
ChatRequest string `gorm:"column:chat_request;comment:保存原始请求的部分字段" json:"chat_request"` // 保存原始请求的部分字段
|
||||
Ext string `gorm:"column:ext;comment:扩展字段" json:"ext"` // 扩展字段
|
||||
Usage *agentrun.Usage `gorm:"column:usage;comment:usage;serializer:json" json:"usage"` // usage
|
||||
}
|
||||
|
||||
// TableName RunRecord's table name
|
||||
func (*RunRecord) TableName() string {
|
||||
return TableNameRunRecord
|
||||
}
|
||||
103
backend/domain/conversation/agentrun/internal/dal/query/gen.go
Normal file
103
backend/domain/conversation/agentrun/internal/dal/query/gen.go
Normal file
@@ -0,0 +1,103 @@
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
|
||||
package query
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"gorm.io/gen"
|
||||
|
||||
"gorm.io/plugin/dbresolver"
|
||||
)
|
||||
|
||||
var (
|
||||
Q = new(Query)
|
||||
RunRecord *runRecord
|
||||
)
|
||||
|
||||
func SetDefault(db *gorm.DB, opts ...gen.DOOption) {
|
||||
*Q = *Use(db, opts...)
|
||||
RunRecord = &Q.RunRecord
|
||||
}
|
||||
|
||||
func Use(db *gorm.DB, opts ...gen.DOOption) *Query {
|
||||
return &Query{
|
||||
db: db,
|
||||
RunRecord: newRunRecord(db, opts...),
|
||||
}
|
||||
}
|
||||
|
||||
type Query struct {
|
||||
db *gorm.DB
|
||||
|
||||
RunRecord runRecord
|
||||
}
|
||||
|
||||
func (q *Query) Available() bool { return q.db != nil }
|
||||
|
||||
func (q *Query) clone(db *gorm.DB) *Query {
|
||||
return &Query{
|
||||
db: db,
|
||||
RunRecord: q.RunRecord.clone(db),
|
||||
}
|
||||
}
|
||||
|
||||
func (q *Query) ReadDB() *Query {
|
||||
return q.ReplaceDB(q.db.Clauses(dbresolver.Read))
|
||||
}
|
||||
|
||||
func (q *Query) WriteDB() *Query {
|
||||
return q.ReplaceDB(q.db.Clauses(dbresolver.Write))
|
||||
}
|
||||
|
||||
func (q *Query) ReplaceDB(db *gorm.DB) *Query {
|
||||
return &Query{
|
||||
db: db,
|
||||
RunRecord: q.RunRecord.replaceDB(db),
|
||||
}
|
||||
}
|
||||
|
||||
type queryCtx struct {
|
||||
RunRecord IRunRecordDo
|
||||
}
|
||||
|
||||
func (q *Query) WithContext(ctx context.Context) *queryCtx {
|
||||
return &queryCtx{
|
||||
RunRecord: q.RunRecord.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
func (q *Query) Transaction(fc func(tx *Query) error, opts ...*sql.TxOptions) error {
|
||||
return q.db.Transaction(func(tx *gorm.DB) error { return fc(q.clone(tx)) }, opts...)
|
||||
}
|
||||
|
||||
func (q *Query) Begin(opts ...*sql.TxOptions) *QueryTx {
|
||||
tx := q.db.Begin(opts...)
|
||||
return &QueryTx{Query: q.clone(tx), Error: tx.Error}
|
||||
}
|
||||
|
||||
type QueryTx struct {
|
||||
*Query
|
||||
Error error
|
||||
}
|
||||
|
||||
func (q *QueryTx) Commit() error {
|
||||
return q.db.Commit().Error
|
||||
}
|
||||
|
||||
func (q *QueryTx) Rollback() error {
|
||||
return q.db.Rollback().Error
|
||||
}
|
||||
|
||||
func (q *QueryTx) SavePoint(name string) error {
|
||||
return q.db.SavePoint(name).Error
|
||||
}
|
||||
|
||||
func (q *QueryTx) RollbackTo(name string) error {
|
||||
return q.db.RollbackTo(name).Error
|
||||
}
|
||||
@@ -0,0 +1,441 @@
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
|
||||
package query
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
"gorm.io/gorm/schema"
|
||||
|
||||
"gorm.io/gen"
|
||||
"gorm.io/gen/field"
|
||||
|
||||
"gorm.io/plugin/dbresolver"
|
||||
|
||||
"github.com/coze-dev/coze-studio/backend/domain/conversation/agentrun/internal/dal/model"
|
||||
)
|
||||
|
||||
func newRunRecord(db *gorm.DB, opts ...gen.DOOption) runRecord {
|
||||
_runRecord := runRecord{}
|
||||
|
||||
_runRecord.runRecordDo.UseDB(db, opts...)
|
||||
_runRecord.runRecordDo.UseModel(&model.RunRecord{})
|
||||
|
||||
tableName := _runRecord.runRecordDo.TableName()
|
||||
_runRecord.ALL = field.NewAsterisk(tableName)
|
||||
_runRecord.ID = field.NewInt64(tableName, "id")
|
||||
_runRecord.ConversationID = field.NewInt64(tableName, "conversation_id")
|
||||
_runRecord.SectionID = field.NewInt64(tableName, "section_id")
|
||||
_runRecord.AgentID = field.NewInt64(tableName, "agent_id")
|
||||
_runRecord.UserID = field.NewString(tableName, "user_id")
|
||||
_runRecord.Source = field.NewInt32(tableName, "source")
|
||||
_runRecord.Status = field.NewString(tableName, "status")
|
||||
_runRecord.CreatorID = field.NewInt64(tableName, "creator_id")
|
||||
_runRecord.CreatedAt = field.NewInt64(tableName, "created_at")
|
||||
_runRecord.UpdatedAt = field.NewInt64(tableName, "updated_at")
|
||||
_runRecord.FailedAt = field.NewInt64(tableName, "failed_at")
|
||||
_runRecord.LastError = field.NewString(tableName, "last_error")
|
||||
_runRecord.CompletedAt = field.NewInt64(tableName, "completed_at")
|
||||
_runRecord.ChatRequest = field.NewString(tableName, "chat_request")
|
||||
_runRecord.Ext = field.NewString(tableName, "ext")
|
||||
_runRecord.Usage = field.NewField(tableName, "usage")
|
||||
|
||||
_runRecord.fillFieldMap()
|
||||
|
||||
return _runRecord
|
||||
}
|
||||
|
||||
// runRecord 执行记录表
|
||||
type runRecord struct {
|
||||
runRecordDo
|
||||
|
||||
ALL field.Asterisk
|
||||
ID field.Int64 // 主键ID
|
||||
ConversationID field.Int64 // 会话 ID
|
||||
SectionID field.Int64 // section ID
|
||||
AgentID field.Int64 // agent_id
|
||||
UserID field.String // user id
|
||||
Source field.Int32 // 执行来源 0 API,
|
||||
Status field.String // 状态,0 Unknown, 1-Created,2-InProgress,3-Completed,4-Failed,5-Expired,6-Cancelled,7-RequiresAction
|
||||
CreatorID field.Int64 // 创建者标识
|
||||
CreatedAt field.Int64 // 创建时间
|
||||
UpdatedAt field.Int64 // 更新时间
|
||||
FailedAt field.Int64 // 失败时间
|
||||
LastError field.String // error message
|
||||
CompletedAt field.Int64 // 结束时间
|
||||
ChatRequest field.String // 保存原始请求的部分字段
|
||||
Ext field.String // 扩展字段
|
||||
Usage field.Field // usage
|
||||
|
||||
fieldMap map[string]field.Expr
|
||||
}
|
||||
|
||||
func (r runRecord) Table(newTableName string) *runRecord {
|
||||
r.runRecordDo.UseTable(newTableName)
|
||||
return r.updateTableName(newTableName)
|
||||
}
|
||||
|
||||
func (r runRecord) As(alias string) *runRecord {
|
||||
r.runRecordDo.DO = *(r.runRecordDo.As(alias).(*gen.DO))
|
||||
return r.updateTableName(alias)
|
||||
}
|
||||
|
||||
func (r *runRecord) updateTableName(table string) *runRecord {
|
||||
r.ALL = field.NewAsterisk(table)
|
||||
r.ID = field.NewInt64(table, "id")
|
||||
r.ConversationID = field.NewInt64(table, "conversation_id")
|
||||
r.SectionID = field.NewInt64(table, "section_id")
|
||||
r.AgentID = field.NewInt64(table, "agent_id")
|
||||
r.UserID = field.NewString(table, "user_id")
|
||||
r.Source = field.NewInt32(table, "source")
|
||||
r.Status = field.NewString(table, "status")
|
||||
r.CreatorID = field.NewInt64(table, "creator_id")
|
||||
r.CreatedAt = field.NewInt64(table, "created_at")
|
||||
r.UpdatedAt = field.NewInt64(table, "updated_at")
|
||||
r.FailedAt = field.NewInt64(table, "failed_at")
|
||||
r.LastError = field.NewString(table, "last_error")
|
||||
r.CompletedAt = field.NewInt64(table, "completed_at")
|
||||
r.ChatRequest = field.NewString(table, "chat_request")
|
||||
r.Ext = field.NewString(table, "ext")
|
||||
r.Usage = field.NewField(table, "usage")
|
||||
|
||||
r.fillFieldMap()
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *runRecord) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
|
||||
_f, ok := r.fieldMap[fieldName]
|
||||
if !ok || _f == nil {
|
||||
return nil, false
|
||||
}
|
||||
_oe, ok := _f.(field.OrderExpr)
|
||||
return _oe, ok
|
||||
}
|
||||
|
||||
func (r *runRecord) fillFieldMap() {
|
||||
r.fieldMap = make(map[string]field.Expr, 16)
|
||||
r.fieldMap["id"] = r.ID
|
||||
r.fieldMap["conversation_id"] = r.ConversationID
|
||||
r.fieldMap["section_id"] = r.SectionID
|
||||
r.fieldMap["agent_id"] = r.AgentID
|
||||
r.fieldMap["user_id"] = r.UserID
|
||||
r.fieldMap["source"] = r.Source
|
||||
r.fieldMap["status"] = r.Status
|
||||
r.fieldMap["creator_id"] = r.CreatorID
|
||||
r.fieldMap["created_at"] = r.CreatedAt
|
||||
r.fieldMap["updated_at"] = r.UpdatedAt
|
||||
r.fieldMap["failed_at"] = r.FailedAt
|
||||
r.fieldMap["last_error"] = r.LastError
|
||||
r.fieldMap["completed_at"] = r.CompletedAt
|
||||
r.fieldMap["chat_request"] = r.ChatRequest
|
||||
r.fieldMap["ext"] = r.Ext
|
||||
r.fieldMap["usage"] = r.Usage
|
||||
}
|
||||
|
||||
func (r runRecord) clone(db *gorm.DB) runRecord {
|
||||
r.runRecordDo.ReplaceConnPool(db.Statement.ConnPool)
|
||||
return r
|
||||
}
|
||||
|
||||
func (r runRecord) replaceDB(db *gorm.DB) runRecord {
|
||||
r.runRecordDo.ReplaceDB(db)
|
||||
return r
|
||||
}
|
||||
|
||||
type runRecordDo struct{ gen.DO }
|
||||
|
||||
type IRunRecordDo interface {
|
||||
gen.SubQuery
|
||||
Debug() IRunRecordDo
|
||||
WithContext(ctx context.Context) IRunRecordDo
|
||||
WithResult(fc func(tx gen.Dao)) gen.ResultInfo
|
||||
ReplaceDB(db *gorm.DB)
|
||||
ReadDB() IRunRecordDo
|
||||
WriteDB() IRunRecordDo
|
||||
As(alias string) gen.Dao
|
||||
Session(config *gorm.Session) IRunRecordDo
|
||||
Columns(cols ...field.Expr) gen.Columns
|
||||
Clauses(conds ...clause.Expression) IRunRecordDo
|
||||
Not(conds ...gen.Condition) IRunRecordDo
|
||||
Or(conds ...gen.Condition) IRunRecordDo
|
||||
Select(conds ...field.Expr) IRunRecordDo
|
||||
Where(conds ...gen.Condition) IRunRecordDo
|
||||
Order(conds ...field.Expr) IRunRecordDo
|
||||
Distinct(cols ...field.Expr) IRunRecordDo
|
||||
Omit(cols ...field.Expr) IRunRecordDo
|
||||
Join(table schema.Tabler, on ...field.Expr) IRunRecordDo
|
||||
LeftJoin(table schema.Tabler, on ...field.Expr) IRunRecordDo
|
||||
RightJoin(table schema.Tabler, on ...field.Expr) IRunRecordDo
|
||||
Group(cols ...field.Expr) IRunRecordDo
|
||||
Having(conds ...gen.Condition) IRunRecordDo
|
||||
Limit(limit int) IRunRecordDo
|
||||
Offset(offset int) IRunRecordDo
|
||||
Count() (count int64, err error)
|
||||
Scopes(funcs ...func(gen.Dao) gen.Dao) IRunRecordDo
|
||||
Unscoped() IRunRecordDo
|
||||
Create(values ...*model.RunRecord) error
|
||||
CreateInBatches(values []*model.RunRecord, batchSize int) error
|
||||
Save(values ...*model.RunRecord) error
|
||||
First() (*model.RunRecord, error)
|
||||
Take() (*model.RunRecord, error)
|
||||
Last() (*model.RunRecord, error)
|
||||
Find() ([]*model.RunRecord, error)
|
||||
FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.RunRecord, err error)
|
||||
FindInBatches(result *[]*model.RunRecord, batchSize int, fc func(tx gen.Dao, batch int) error) error
|
||||
Pluck(column field.Expr, dest interface{}) error
|
||||
Delete(...*model.RunRecord) (info gen.ResultInfo, err error)
|
||||
Update(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
|
||||
UpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
|
||||
Updates(value interface{}) (info gen.ResultInfo, err error)
|
||||
UpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
|
||||
UpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
|
||||
UpdateColumns(value interface{}) (info gen.ResultInfo, err error)
|
||||
UpdateFrom(q gen.SubQuery) gen.Dao
|
||||
Attrs(attrs ...field.AssignExpr) IRunRecordDo
|
||||
Assign(attrs ...field.AssignExpr) IRunRecordDo
|
||||
Joins(fields ...field.RelationField) IRunRecordDo
|
||||
Preload(fields ...field.RelationField) IRunRecordDo
|
||||
FirstOrInit() (*model.RunRecord, error)
|
||||
FirstOrCreate() (*model.RunRecord, error)
|
||||
FindByPage(offset int, limit int) (result []*model.RunRecord, count int64, err error)
|
||||
ScanByPage(result interface{}, offset int, limit int) (count int64, err error)
|
||||
Scan(result interface{}) (err error)
|
||||
Returning(value interface{}, columns ...string) IRunRecordDo
|
||||
UnderlyingDB() *gorm.DB
|
||||
schema.Tabler
|
||||
}
|
||||
|
||||
func (r runRecordDo) Debug() IRunRecordDo {
|
||||
return r.withDO(r.DO.Debug())
|
||||
}
|
||||
|
||||
func (r runRecordDo) WithContext(ctx context.Context) IRunRecordDo {
|
||||
return r.withDO(r.DO.WithContext(ctx))
|
||||
}
|
||||
|
||||
func (r runRecordDo) ReadDB() IRunRecordDo {
|
||||
return r.Clauses(dbresolver.Read)
|
||||
}
|
||||
|
||||
func (r runRecordDo) WriteDB() IRunRecordDo {
|
||||
return r.Clauses(dbresolver.Write)
|
||||
}
|
||||
|
||||
func (r runRecordDo) Session(config *gorm.Session) IRunRecordDo {
|
||||
return r.withDO(r.DO.Session(config))
|
||||
}
|
||||
|
||||
func (r runRecordDo) Clauses(conds ...clause.Expression) IRunRecordDo {
|
||||
return r.withDO(r.DO.Clauses(conds...))
|
||||
}
|
||||
|
||||
func (r runRecordDo) Returning(value interface{}, columns ...string) IRunRecordDo {
|
||||
return r.withDO(r.DO.Returning(value, columns...))
|
||||
}
|
||||
|
||||
func (r runRecordDo) Not(conds ...gen.Condition) IRunRecordDo {
|
||||
return r.withDO(r.DO.Not(conds...))
|
||||
}
|
||||
|
||||
func (r runRecordDo) Or(conds ...gen.Condition) IRunRecordDo {
|
||||
return r.withDO(r.DO.Or(conds...))
|
||||
}
|
||||
|
||||
func (r runRecordDo) Select(conds ...field.Expr) IRunRecordDo {
|
||||
return r.withDO(r.DO.Select(conds...))
|
||||
}
|
||||
|
||||
func (r runRecordDo) Where(conds ...gen.Condition) IRunRecordDo {
|
||||
return r.withDO(r.DO.Where(conds...))
|
||||
}
|
||||
|
||||
func (r runRecordDo) Order(conds ...field.Expr) IRunRecordDo {
|
||||
return r.withDO(r.DO.Order(conds...))
|
||||
}
|
||||
|
||||
func (r runRecordDo) Distinct(cols ...field.Expr) IRunRecordDo {
|
||||
return r.withDO(r.DO.Distinct(cols...))
|
||||
}
|
||||
|
||||
func (r runRecordDo) Omit(cols ...field.Expr) IRunRecordDo {
|
||||
return r.withDO(r.DO.Omit(cols...))
|
||||
}
|
||||
|
||||
func (r runRecordDo) Join(table schema.Tabler, on ...field.Expr) IRunRecordDo {
|
||||
return r.withDO(r.DO.Join(table, on...))
|
||||
}
|
||||
|
||||
func (r runRecordDo) LeftJoin(table schema.Tabler, on ...field.Expr) IRunRecordDo {
|
||||
return r.withDO(r.DO.LeftJoin(table, on...))
|
||||
}
|
||||
|
||||
func (r runRecordDo) RightJoin(table schema.Tabler, on ...field.Expr) IRunRecordDo {
|
||||
return r.withDO(r.DO.RightJoin(table, on...))
|
||||
}
|
||||
|
||||
func (r runRecordDo) Group(cols ...field.Expr) IRunRecordDo {
|
||||
return r.withDO(r.DO.Group(cols...))
|
||||
}
|
||||
|
||||
func (r runRecordDo) Having(conds ...gen.Condition) IRunRecordDo {
|
||||
return r.withDO(r.DO.Having(conds...))
|
||||
}
|
||||
|
||||
func (r runRecordDo) Limit(limit int) IRunRecordDo {
|
||||
return r.withDO(r.DO.Limit(limit))
|
||||
}
|
||||
|
||||
func (r runRecordDo) Offset(offset int) IRunRecordDo {
|
||||
return r.withDO(r.DO.Offset(offset))
|
||||
}
|
||||
|
||||
func (r runRecordDo) Scopes(funcs ...func(gen.Dao) gen.Dao) IRunRecordDo {
|
||||
return r.withDO(r.DO.Scopes(funcs...))
|
||||
}
|
||||
|
||||
func (r runRecordDo) Unscoped() IRunRecordDo {
|
||||
return r.withDO(r.DO.Unscoped())
|
||||
}
|
||||
|
||||
func (r runRecordDo) Create(values ...*model.RunRecord) error {
|
||||
if len(values) == 0 {
|
||||
return nil
|
||||
}
|
||||
return r.DO.Create(values)
|
||||
}
|
||||
|
||||
func (r runRecordDo) CreateInBatches(values []*model.RunRecord, batchSize int) error {
|
||||
return r.DO.CreateInBatches(values, batchSize)
|
||||
}
|
||||
|
||||
// Save : !!! underlying implementation is different with GORM
|
||||
// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
|
||||
func (r runRecordDo) Save(values ...*model.RunRecord) error {
|
||||
if len(values) == 0 {
|
||||
return nil
|
||||
}
|
||||
return r.DO.Save(values)
|
||||
}
|
||||
|
||||
func (r runRecordDo) First() (*model.RunRecord, error) {
|
||||
if result, err := r.DO.First(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*model.RunRecord), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (r runRecordDo) Take() (*model.RunRecord, error) {
|
||||
if result, err := r.DO.Take(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*model.RunRecord), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (r runRecordDo) Last() (*model.RunRecord, error) {
|
||||
if result, err := r.DO.Last(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*model.RunRecord), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (r runRecordDo) Find() ([]*model.RunRecord, error) {
|
||||
result, err := r.DO.Find()
|
||||
return result.([]*model.RunRecord), err
|
||||
}
|
||||
|
||||
func (r runRecordDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.RunRecord, err error) {
|
||||
buf := make([]*model.RunRecord, 0, batchSize)
|
||||
err = r.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
|
||||
defer func() { results = append(results, buf...) }()
|
||||
return fc(tx, batch)
|
||||
})
|
||||
return results, err
|
||||
}
|
||||
|
||||
func (r runRecordDo) FindInBatches(result *[]*model.RunRecord, batchSize int, fc func(tx gen.Dao, batch int) error) error {
|
||||
return r.DO.FindInBatches(result, batchSize, fc)
|
||||
}
|
||||
|
||||
func (r runRecordDo) Attrs(attrs ...field.AssignExpr) IRunRecordDo {
|
||||
return r.withDO(r.DO.Attrs(attrs...))
|
||||
}
|
||||
|
||||
func (r runRecordDo) Assign(attrs ...field.AssignExpr) IRunRecordDo {
|
||||
return r.withDO(r.DO.Assign(attrs...))
|
||||
}
|
||||
|
||||
func (r runRecordDo) Joins(fields ...field.RelationField) IRunRecordDo {
|
||||
for _, _f := range fields {
|
||||
r = *r.withDO(r.DO.Joins(_f))
|
||||
}
|
||||
return &r
|
||||
}
|
||||
|
||||
func (r runRecordDo) Preload(fields ...field.RelationField) IRunRecordDo {
|
||||
for _, _f := range fields {
|
||||
r = *r.withDO(r.DO.Preload(_f))
|
||||
}
|
||||
return &r
|
||||
}
|
||||
|
||||
func (r runRecordDo) FirstOrInit() (*model.RunRecord, error) {
|
||||
if result, err := r.DO.FirstOrInit(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*model.RunRecord), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (r runRecordDo) FirstOrCreate() (*model.RunRecord, error) {
|
||||
if result, err := r.DO.FirstOrCreate(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*model.RunRecord), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (r runRecordDo) FindByPage(offset int, limit int) (result []*model.RunRecord, count int64, err error) {
|
||||
result, err = r.Offset(offset).Limit(limit).Find()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if size := len(result); 0 < limit && 0 < size && size < limit {
|
||||
count = int64(size + offset)
|
||||
return
|
||||
}
|
||||
|
||||
count, err = r.Offset(-1).Limit(-1).Count()
|
||||
return
|
||||
}
|
||||
|
||||
func (r runRecordDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
|
||||
count, err = r.Count()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = r.Offset(offset).Limit(limit).Scan(result)
|
||||
return
|
||||
}
|
||||
|
||||
func (r runRecordDo) Scan(result interface{}) (err error) {
|
||||
return r.DO.Scan(result)
|
||||
}
|
||||
|
||||
func (r runRecordDo) Delete(models ...*model.RunRecord) (result gen.ResultInfo, err error) {
|
||||
return r.DO.Delete(models)
|
||||
}
|
||||
|
||||
func (r *runRecordDo) withDO(do gen.Dao) *runRecordDo {
|
||||
r.DO = *do.(*gen.DO)
|
||||
return r
|
||||
}
|
||||
78
backend/domain/conversation/agentrun/internal/event.go
Normal file
78
backend/domain/conversation/agentrun/internal/event.go
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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 internal
|
||||
|
||||
import (
|
||||
"github.com/cloudwego/eino/schema"
|
||||
|
||||
"github.com/coze-dev/coze-studio/backend/domain/conversation/agentrun/entity"
|
||||
)
|
||||
|
||||
type Event struct {
|
||||
}
|
||||
|
||||
func NewEvent() *Event {
|
||||
return &Event{}
|
||||
}
|
||||
|
||||
func (e *Event) buildMessageEvent(runEvent entity.RunEvent, chunkMsgItem *entity.ChunkMessageItem) *entity.AgentRunResponse {
|
||||
return &entity.AgentRunResponse{
|
||||
Event: runEvent,
|
||||
ChunkMessageItem: chunkMsgItem,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Event) buildRunEvent(runEvent entity.RunEvent, chunkRunItem *entity.ChunkRunItem) *entity.AgentRunResponse {
|
||||
return &entity.AgentRunResponse{
|
||||
Event: runEvent,
|
||||
ChunkRunItem: chunkRunItem,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Event) buildErrEvent(runEvent entity.RunEvent, err *entity.RunError) *entity.AgentRunResponse {
|
||||
return &entity.AgentRunResponse{
|
||||
Event: runEvent,
|
||||
Error: err,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Event) buildStreamDoneEvent() *entity.AgentRunResponse {
|
||||
|
||||
return &entity.AgentRunResponse{
|
||||
Event: entity.RunEventStreamDone,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Event) SendRunEvent(runEvent entity.RunEvent, runItem *entity.ChunkRunItem, sw *schema.StreamWriter[*entity.AgentRunResponse]) {
|
||||
resp := e.buildRunEvent(runEvent, runItem)
|
||||
sw.Send(resp, nil)
|
||||
}
|
||||
|
||||
func (e *Event) SendMsgEvent(runEvent entity.RunEvent, messageItem *entity.ChunkMessageItem, sw *schema.StreamWriter[*entity.AgentRunResponse]) {
|
||||
resp := e.buildMessageEvent(runEvent, messageItem)
|
||||
sw.Send(resp, nil)
|
||||
}
|
||||
|
||||
func (e *Event) SendErrEvent(runEvent entity.RunEvent, sw *schema.StreamWriter[*entity.AgentRunResponse], err *entity.RunError) {
|
||||
resp := e.buildErrEvent(runEvent, err)
|
||||
sw.Send(resp, nil)
|
||||
}
|
||||
|
||||
func (e *Event) SendStreamDoneEvent(sw *schema.StreamWriter[*entity.AgentRunResponse]) {
|
||||
resp := e.buildStreamDoneEvent()
|
||||
sw.Send(resp, nil)
|
||||
}
|
||||
123
backend/domain/conversation/agentrun/internal/run_process.go
Normal file
123
backend/domain/conversation/agentrun/internal/run_process.go
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* 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 internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/cloudwego/eino/schema"
|
||||
|
||||
"github.com/coze-dev/coze-studio/backend/api/model/crossdomain/agentrun"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/conversation/agentrun/entity"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/conversation/agentrun/repository"
|
||||
"github.com/coze-dev/coze-studio/backend/pkg/logs"
|
||||
"github.com/coze-dev/coze-studio/backend/types/errno"
|
||||
)
|
||||
|
||||
type RunProcess struct {
|
||||
event *Event
|
||||
|
||||
RunRecordRepo repository.RunRecordRepo
|
||||
}
|
||||
|
||||
func NewRunProcess(runRecordRepo repository.RunRecordRepo) *RunProcess {
|
||||
return &RunProcess{
|
||||
RunRecordRepo: runRecordRepo,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RunProcess) StepToCreate(ctx context.Context, srRecord *entity.ChunkRunItem, sw *schema.StreamWriter[*entity.AgentRunResponse]) {
|
||||
srRecord.Status = entity.RunStatusCreated
|
||||
r.event.SendRunEvent(entity.RunEventCreated, srRecord, sw)
|
||||
}
|
||||
func (r *RunProcess) StepToInProgress(ctx context.Context, srRecord *entity.ChunkRunItem, sw *schema.StreamWriter[*entity.AgentRunResponse]) error {
|
||||
srRecord.Status = entity.RunStatusInProgress
|
||||
|
||||
updateMeta := &entity.UpdateMeta{
|
||||
Status: entity.RunStatusInProgress,
|
||||
UpdatedAt: time.Now().UnixMilli(),
|
||||
}
|
||||
err := r.RunRecordRepo.UpdateByID(ctx, srRecord.ID, updateMeta)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.event.SendRunEvent(entity.RunEventInProgress, srRecord, sw)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *RunProcess) StepToComplete(ctx context.Context, srRecord *entity.ChunkRunItem, sw *schema.StreamWriter[*entity.AgentRunResponse], usage *agentrun.Usage) {
|
||||
|
||||
completedAt := time.Now().UnixMilli()
|
||||
|
||||
updateMeta := &entity.UpdateMeta{
|
||||
Status: entity.RunStatusCompleted,
|
||||
Usage: usage,
|
||||
CompletedAt: completedAt,
|
||||
UpdatedAt: completedAt,
|
||||
}
|
||||
err := r.RunRecordRepo.UpdateByID(ctx, srRecord.ID, updateMeta)
|
||||
if err != nil {
|
||||
logs.CtxErrorf(ctx, "RunRecordRepo.UpdateByID error: %v", err)
|
||||
r.event.SendErrEvent(entity.RunEventError, sw, &entity.RunError{
|
||||
Code: errno.ErrConversationAgentRunError,
|
||||
Msg: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
srRecord.CompletedAt = completedAt
|
||||
srRecord.Status = entity.RunStatusCompleted
|
||||
|
||||
r.event.SendRunEvent(entity.RunEventCompleted, srRecord, sw)
|
||||
|
||||
r.event.SendStreamDoneEvent(sw)
|
||||
}
|
||||
func (r *RunProcess) StepToFailed(ctx context.Context, srRecord *entity.ChunkRunItem, sw *schema.StreamWriter[*entity.AgentRunResponse]) {
|
||||
|
||||
nowTime := time.Now().UnixMilli()
|
||||
updateMeta := &entity.UpdateMeta{
|
||||
Status: entity.RunStatusFailed,
|
||||
UpdatedAt: nowTime,
|
||||
FailedAt: nowTime,
|
||||
LastError: srRecord.Error,
|
||||
}
|
||||
|
||||
err := r.RunRecordRepo.UpdateByID(ctx, srRecord.ID, updateMeta)
|
||||
|
||||
if err != nil {
|
||||
r.event.SendErrEvent(entity.RunEventError, sw, &entity.RunError{
|
||||
Code: errno.ErrConversationAgentRunError,
|
||||
Msg: err.Error(),
|
||||
})
|
||||
logs.CtxErrorf(ctx, "update run record failed, err: %v", err)
|
||||
return
|
||||
}
|
||||
srRecord.Status = entity.RunStatusFailed
|
||||
srRecord.FailedAt = time.Now().UnixMilli()
|
||||
r.event.SendErrEvent(entity.RunEventError, sw, &entity.RunError{
|
||||
Code: srRecord.Error.Code,
|
||||
Msg: srRecord.Error.Msg,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
func (r *RunProcess) StepToDone(sw *schema.StreamWriter[*entity.AgentRunResponse]) {
|
||||
r.event.SendStreamDoneEvent(sw)
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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 repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/coze-dev/coze-studio/backend/domain/conversation/agentrun/entity"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/conversation/agentrun/internal/dal"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/conversation/agentrun/internal/dal/model"
|
||||
"github.com/coze-dev/coze-studio/backend/infra/contract/idgen"
|
||||
)
|
||||
|
||||
func NewRunRecordRepo(db *gorm.DB, idGen idgen.IDGenerator) RunRecordRepo {
|
||||
|
||||
return dal.NewRunRecordDAO(db, idGen)
|
||||
}
|
||||
|
||||
type RunRecordRepo interface {
|
||||
Create(ctx context.Context, runMeta *entity.AgentRunMeta) (*entity.RunRecordMeta, error)
|
||||
GetByID(ctx context.Context, id int64) (*entity.RunRecord, error)
|
||||
Delete(ctx context.Context, id []int64) error
|
||||
UpdateByID(ctx context.Context, id int64, update *entity.UpdateMeta) error
|
||||
List(ctx context.Context, conversationID int64, sectionID int64, limit int32) ([]*model.RunRecord, error)
|
||||
}
|
||||
31
backend/domain/conversation/agentrun/service/agent_run.go
Normal file
31
backend/domain/conversation/agentrun/service/agent_run.go
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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 agentrun
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/cloudwego/eino/schema"
|
||||
|
||||
"github.com/coze-dev/coze-studio/backend/domain/conversation/agentrun/entity"
|
||||
)
|
||||
|
||||
type Run interface {
|
||||
AgentRun(ctx context.Context, req *entity.AgentRunMeta) (*schema.StreamReader[*entity.AgentRunResponse], error)
|
||||
|
||||
Delete(ctx context.Context, runID []int64) error
|
||||
}
|
||||
994
backend/domain/conversation/agentrun/service/agent_run_impl.go
Normal file
994
backend/domain/conversation/agentrun/service/agent_run_impl.go
Normal file
@@ -0,0 +1,994 @@
|
||||
/*
|
||||
* 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 agentrun
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/cloudwego/eino/schema"
|
||||
"github.com/mohae/deepcopy"
|
||||
|
||||
messageModel "github.com/coze-dev/coze-studio/backend/api/model/conversation/message"
|
||||
"github.com/coze-dev/coze-studio/backend/api/model/crossdomain/agentrun"
|
||||
"github.com/coze-dev/coze-studio/backend/api/model/crossdomain/message"
|
||||
"github.com/coze-dev/coze-studio/backend/api/model/crossdomain/singleagent"
|
||||
"github.com/coze-dev/coze-studio/backend/crossdomain/contract/crossagent"
|
||||
"github.com/coze-dev/coze-studio/backend/crossdomain/contract/crossmessage"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/conversation/agentrun/entity"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/conversation/agentrun/internal"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/conversation/agentrun/internal/dal/model"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/conversation/agentrun/repository"
|
||||
msgEntity "github.com/coze-dev/coze-studio/backend/domain/conversation/message/entity"
|
||||
"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"
|
||||
"github.com/coze-dev/coze-studio/backend/pkg/safego"
|
||||
"github.com/coze-dev/coze-studio/backend/types/errno"
|
||||
)
|
||||
|
||||
type runImpl struct {
|
||||
Components
|
||||
|
||||
runProcess *internal.RunProcess
|
||||
runEvent *internal.Event
|
||||
}
|
||||
|
||||
type runtimeDependence struct {
|
||||
runID int64
|
||||
agentInfo *singleagent.SingleAgent
|
||||
questionMsgID int64
|
||||
runMeta *entity.AgentRunMeta
|
||||
startTime time.Time
|
||||
|
||||
usage *agentrun.Usage
|
||||
}
|
||||
|
||||
type Components struct {
|
||||
RunRecordRepo repository.RunRecordRepo
|
||||
}
|
||||
|
||||
func NewService(c *Components) Run {
|
||||
return &runImpl{
|
||||
Components: *c,
|
||||
runEvent: internal.NewEvent(),
|
||||
runProcess: internal.NewRunProcess(c.RunRecordRepo),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *runImpl) AgentRun(ctx context.Context, arm *entity.AgentRunMeta) (*schema.StreamReader[*entity.AgentRunResponse], error) {
|
||||
sr, sw := schema.Pipe[*entity.AgentRunResponse](20)
|
||||
|
||||
defer func() {
|
||||
if pe := recover(); pe != nil {
|
||||
logs.CtxErrorf(ctx, "panic recover: %v\n, [stack]:%v", pe, string(debug.Stack()))
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
rtDependence := &runtimeDependence{
|
||||
runMeta: arm,
|
||||
startTime: time.Now(),
|
||||
}
|
||||
|
||||
safego.Go(ctx, func() {
|
||||
defer sw.Close()
|
||||
_ = c.run(ctx, sw, rtDependence)
|
||||
})
|
||||
|
||||
return sr, nil
|
||||
}
|
||||
|
||||
func (c *runImpl) run(ctx context.Context, sw *schema.StreamWriter[*entity.AgentRunResponse], rtDependence *runtimeDependence) (err error) {
|
||||
runRecord, err := c.createRunRecord(ctx, sw, rtDependence)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
rtDependence.runID = runRecord.ID
|
||||
defer func() {
|
||||
srRecord := c.buildSendRunRecord(ctx, runRecord, entity.RunStatusCompleted)
|
||||
if err != nil {
|
||||
srRecord.Error = &entity.RunError{
|
||||
Code: errno.ErrConversationAgentRunError,
|
||||
Msg: err.Error(),
|
||||
}
|
||||
c.runProcess.StepToFailed(ctx, srRecord, sw)
|
||||
return
|
||||
}
|
||||
c.runProcess.StepToComplete(ctx, srRecord, sw, rtDependence.usage)
|
||||
}()
|
||||
|
||||
agentInfo, err := c.handlerAgent(ctx, rtDependence)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
rtDependence.agentInfo = agentInfo
|
||||
|
||||
history, err := c.handlerHistory(ctx, rtDependence)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
input, err := c.handlerInput(ctx, sw, rtDependence)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
rtDependence.questionMsgID = input.ID
|
||||
|
||||
err = c.handlerStreamExecute(ctx, sw, history, input, rtDependence)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *runImpl) handlerAgent(ctx context.Context, rtDependence *runtimeDependence) (*singleagent.SingleAgent, error) {
|
||||
agentInfo, err := crossagent.DefaultSVC().ObtainAgentByIdentity(ctx, &singleagent.AgentIdentity{
|
||||
AgentID: rtDependence.runMeta.AgentID,
|
||||
IsDraft: rtDependence.runMeta.IsDraft,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return agentInfo, nil
|
||||
}
|
||||
|
||||
func (c *runImpl) handlerStreamExecute(ctx context.Context, sw *schema.StreamWriter[*entity.AgentRunResponse], historyMsg []*msgEntity.Message, input *msgEntity.Message, rtDependence *runtimeDependence) (err error) {
|
||||
mainChan := make(chan *entity.AgentRespEvent, 100)
|
||||
|
||||
ar := &singleagent.AgentRuntime{
|
||||
AgentVersion: rtDependence.runMeta.Version,
|
||||
SpaceID: rtDependence.runMeta.SpaceID,
|
||||
IsDraft: rtDependence.runMeta.IsDraft,
|
||||
ConnectorID: rtDependence.runMeta.ConnectorID,
|
||||
PreRetrieveTools: rtDependence.runMeta.PreRetrieveTools,
|
||||
}
|
||||
|
||||
streamer, err := crossagent.DefaultSVC().StreamExecute(ctx, historyMsg, input, ar)
|
||||
if err != nil {
|
||||
return errors.New(errorx.ErrorWithoutStack(err))
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
safego.Go(ctx, func() {
|
||||
defer wg.Done()
|
||||
c.pull(ctx, mainChan, streamer)
|
||||
})
|
||||
|
||||
safego.Go(ctx, func() {
|
||||
defer wg.Done()
|
||||
c.push(ctx, mainChan, sw, rtDependence)
|
||||
})
|
||||
|
||||
wg.Wait()
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func transformEventMap(eventType singleagent.EventType) (message.MessageType, error) {
|
||||
var eType message.MessageType
|
||||
switch eventType {
|
||||
case singleagent.EventTypeOfFuncCall:
|
||||
return message.MessageTypeFunctionCall, nil
|
||||
case singleagent.EventTypeOfKnowledge:
|
||||
return message.MessageTypeKnowledge, nil
|
||||
case singleagent.EventTypeOfToolsMessage:
|
||||
return message.MessageTypeToolResponse, nil
|
||||
case singleagent.EventTypeOfChatModelAnswer:
|
||||
return message.MessageTypeAnswer, nil
|
||||
case singleagent.EventTypeOfSuggest:
|
||||
return message.MessageTypeFlowUp, nil
|
||||
case singleagent.EventTypeOfInterrupt:
|
||||
return message.MessageTypeInterrupt, nil
|
||||
}
|
||||
return eType, errorx.New(errno.ErrReplyUnknowEventType)
|
||||
}
|
||||
|
||||
func (c *runImpl) buildAgentMessage2Create(ctx context.Context, chunk *entity.AgentRespEvent, messageType message.MessageType, rtDependence *runtimeDependence) *message.Message {
|
||||
arm := rtDependence.runMeta
|
||||
msg := &msgEntity.Message{
|
||||
ConversationID: arm.ConversationID,
|
||||
RunID: rtDependence.runID,
|
||||
AgentID: arm.AgentID,
|
||||
SectionID: arm.SectionID,
|
||||
UserID: arm.UserID,
|
||||
MessageType: messageType,
|
||||
}
|
||||
buildExt := map[string]string{}
|
||||
|
||||
timeCost := fmt.Sprintf("%.1f", float64(time.Since(rtDependence.startTime).Milliseconds())/1000.00)
|
||||
|
||||
switch messageType {
|
||||
case message.MessageTypeQuestion:
|
||||
msg.Role = schema.User
|
||||
msg.ContentType = arm.ContentType
|
||||
for _, content := range arm.Content {
|
||||
if content.Type == message.InputTypeText {
|
||||
msg.Content = content.Text
|
||||
break
|
||||
}
|
||||
}
|
||||
msg.MultiContent = arm.Content
|
||||
buildExt = arm.Ext
|
||||
|
||||
msg.DisplayContent = arm.DisplayContent
|
||||
case message.MessageTypeAnswer:
|
||||
msg.Role = schema.Assistant
|
||||
msg.ContentType = message.ContentTypeText
|
||||
|
||||
case message.MessageTypeToolResponse:
|
||||
msg.Role = schema.Tool
|
||||
msg.ContentType = message.ContentTypeText
|
||||
msg.Content = chunk.ToolsMessage[0].Content
|
||||
|
||||
buildExt[string(msgEntity.MessageExtKeyTimeCost)] = timeCost
|
||||
modelContent := chunk.ToolsMessage[0]
|
||||
mc, err := json.Marshal(modelContent)
|
||||
if err == nil {
|
||||
msg.ModelContent = string(mc)
|
||||
}
|
||||
|
||||
case message.MessageTypeKnowledge:
|
||||
msg.Role = schema.Assistant
|
||||
msg.ContentType = message.ContentTypeText
|
||||
|
||||
knowledgeContent := c.buildKnowledge(ctx, arm, chunk)
|
||||
if knowledgeContent != nil {
|
||||
knInfo, err := json.Marshal(knowledgeContent)
|
||||
if err == nil {
|
||||
msg.Content = string(knInfo)
|
||||
}
|
||||
}
|
||||
|
||||
buildExt[string(msgEntity.MessageExtKeyTimeCost)] = timeCost
|
||||
|
||||
modelContent := chunk.Knowledge
|
||||
mc, err := json.Marshal(modelContent)
|
||||
if err == nil {
|
||||
msg.ModelContent = string(mc)
|
||||
}
|
||||
|
||||
case message.MessageTypeFunctionCall:
|
||||
msg.Role = schema.Assistant
|
||||
msg.ContentType = message.ContentTypeText
|
||||
|
||||
if len(chunk.FuncCall.ToolCalls) > 0 {
|
||||
toolCall := chunk.FuncCall.ToolCalls[0]
|
||||
toolCalling, err := json.Marshal(toolCall)
|
||||
if err == nil {
|
||||
msg.Content = string(toolCalling)
|
||||
}
|
||||
buildExt[string(msgEntity.MessageExtKeyPlugin)] = toolCall.Function.Name
|
||||
buildExt[string(msgEntity.MessageExtKeyToolName)] = toolCall.Function.Name
|
||||
buildExt[string(msgEntity.MessageExtKeyTimeCost)] = timeCost
|
||||
|
||||
modelContent := chunk.FuncCall
|
||||
mc, err := json.Marshal(modelContent)
|
||||
if err == nil {
|
||||
msg.ModelContent = string(mc)
|
||||
}
|
||||
}
|
||||
case message.MessageTypeFlowUp:
|
||||
msg.Role = schema.Assistant
|
||||
msg.ContentType = message.ContentTypeText
|
||||
msg.Content = chunk.Suggest.Content
|
||||
|
||||
case message.MessageTypeVerbose:
|
||||
msg.Role = schema.Assistant
|
||||
msg.ContentType = message.ContentTypeText
|
||||
|
||||
d := &entity.Data{
|
||||
FinishReason: 0,
|
||||
FinData: "",
|
||||
}
|
||||
dByte, _ := json.Marshal(d)
|
||||
afc := &entity.AnswerFinshContent{
|
||||
MsgType: entity.MessageSubTypeGenerateFinish,
|
||||
Data: string(dByte),
|
||||
}
|
||||
afcMarshal, _ := json.Marshal(afc)
|
||||
msg.Content = string(afcMarshal)
|
||||
case message.MessageTypeInterrupt:
|
||||
msg.Role = schema.Assistant
|
||||
msg.MessageType = message.MessageTypeVerbose
|
||||
msg.ContentType = message.ContentTypeText
|
||||
|
||||
afc := &entity.AnswerFinshContent{
|
||||
MsgType: entity.MessageSubTypeInterrupt,
|
||||
Data: "",
|
||||
}
|
||||
afcMarshal, _ := json.Marshal(afc)
|
||||
msg.Content = string(afcMarshal)
|
||||
|
||||
// 添加 ext 用于保存到 context_message
|
||||
interruptByte, err := json.Marshal(chunk.Interrupt)
|
||||
if err == nil {
|
||||
buildExt[string(msgEntity.ExtKeyResumeInfo)] = string(interruptByte)
|
||||
}
|
||||
buildExt[string(msgEntity.ExtKeyToolCallsIDs)] = chunk.Interrupt.ToolCallID
|
||||
rc := &messageModel.RequiredAction{
|
||||
Type: "submit_tool_outputs",
|
||||
SubmitToolOutputs: &messageModel.SubmitToolOutputs{},
|
||||
}
|
||||
msg.RequiredAction = rc
|
||||
rcExtByte, err := json.Marshal(rc)
|
||||
if err == nil {
|
||||
buildExt[string(msgEntity.ExtKeyRequiresAction)] = string(rcExtByte)
|
||||
}
|
||||
}
|
||||
|
||||
if messageType != message.MessageTypeQuestion {
|
||||
botStateExt := c.buildBotStateExt(arm)
|
||||
bseString, err := json.Marshal(botStateExt)
|
||||
if err == nil {
|
||||
buildExt[string(msgEntity.MessageExtKeyBotState)] = string(bseString)
|
||||
}
|
||||
}
|
||||
msg.Ext = buildExt
|
||||
return msg
|
||||
}
|
||||
|
||||
func (c *runImpl) handlerHistory(ctx context.Context, rtDependence *runtimeDependence) ([]*msgEntity.Message, error) {
|
||||
|
||||
conversationTurns := entity.ConversationTurnsDefault
|
||||
|
||||
if rtDependence.agentInfo != nil && rtDependence.agentInfo.ModelInfo != nil && rtDependence.agentInfo.ModelInfo.ShortMemoryPolicy != nil && ptr.From(rtDependence.agentInfo.ModelInfo.ShortMemoryPolicy.HistoryRound) > 0 {
|
||||
conversationTurns = ptr.From(rtDependence.agentInfo.ModelInfo.ShortMemoryPolicy.HistoryRound)
|
||||
}
|
||||
|
||||
runRecordList, err := c.RunRecordRepo.List(ctx, rtDependence.runMeta.ConversationID, rtDependence.runMeta.SectionID, conversationTurns)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(runRecordList) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
runIDS := c.getRunID(runRecordList)
|
||||
|
||||
history, err := crossmessage.DefaultSVC().GetByRunIDs(ctx, rtDependence.runMeta.ConversationID, runIDS)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return history, nil
|
||||
}
|
||||
|
||||
func (c *runImpl) getRunID(rr []*model.RunRecord) []int64 {
|
||||
ids := make([]int64, 0, len(rr))
|
||||
for _, c := range rr {
|
||||
ids = append(ids, c.ID)
|
||||
}
|
||||
|
||||
return ids
|
||||
}
|
||||
|
||||
func (c *runImpl) createRunRecord(ctx context.Context, sw *schema.StreamWriter[*entity.AgentRunResponse], rtDependence *runtimeDependence) (*entity.RunRecordMeta, error) {
|
||||
runPoData, err := c.RunRecordRepo.Create(ctx, rtDependence.runMeta)
|
||||
if err != nil {
|
||||
logs.CtxErrorf(ctx, "RunRecordRepo.Create error: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
srRecord := c.buildSendRunRecord(ctx, runPoData, entity.RunStatusCreated)
|
||||
|
||||
c.runProcess.StepToCreate(ctx, srRecord, sw)
|
||||
|
||||
err = c.runProcess.StepToInProgress(ctx, srRecord, sw)
|
||||
if err != nil {
|
||||
logs.CtxErrorf(ctx, "runProcess.StepToInProgress error: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return runPoData, nil
|
||||
}
|
||||
|
||||
func (c *runImpl) handlerInput(ctx context.Context, sw *schema.StreamWriter[*entity.AgentRunResponse], rtDependence *runtimeDependence) (*msgEntity.Message, error) {
|
||||
msgMeta := c.buildAgentMessage2Create(ctx, nil, message.MessageTypeQuestion, rtDependence)
|
||||
|
||||
cm, err := crossmessage.DefaultSVC().Create(ctx, msgMeta)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ackErr := c.handlerAckMessage(ctx, cm, sw)
|
||||
if ackErr != nil {
|
||||
return msgMeta, ackErr
|
||||
}
|
||||
return cm, nil
|
||||
}
|
||||
|
||||
func (c *runImpl) pull(_ context.Context, mainChan chan *entity.AgentRespEvent, events *schema.StreamReader[*crossagent.AgentEvent]) {
|
||||
defer func() {
|
||||
close(mainChan)
|
||||
}()
|
||||
|
||||
for {
|
||||
rm, re := events.Recv()
|
||||
if re != nil {
|
||||
errChunk := &entity.AgentRespEvent{
|
||||
Err: re,
|
||||
}
|
||||
mainChan <- errChunk
|
||||
return
|
||||
}
|
||||
|
||||
eventType, tErr := transformEventMap(rm.EventType)
|
||||
|
||||
if tErr != nil {
|
||||
errChunk := &entity.AgentRespEvent{
|
||||
Err: tErr,
|
||||
}
|
||||
mainChan <- errChunk
|
||||
return
|
||||
}
|
||||
|
||||
respChunk := &entity.AgentRespEvent{
|
||||
EventType: eventType,
|
||||
ModelAnswer: rm.ChatModelAnswer,
|
||||
ToolsMessage: rm.ToolsMessage,
|
||||
FuncCall: rm.FuncCall,
|
||||
Knowledge: rm.Knowledge,
|
||||
Suggest: rm.Suggest,
|
||||
Interrupt: rm.Interrupt,
|
||||
}
|
||||
|
||||
mainChan <- respChunk
|
||||
}
|
||||
}
|
||||
|
||||
func (c *runImpl) push(ctx context.Context, mainChan chan *entity.AgentRespEvent, sw *schema.StreamWriter[*entity.AgentRunResponse], rtDependence *runtimeDependence) {
|
||||
|
||||
var err error
|
||||
defer func() {
|
||||
if err != nil {
|
||||
logs.CtxErrorf(ctx, "run.push error: %v", err)
|
||||
c.handlerErr(ctx, err, sw)
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
chunk, ok := <-mainChan
|
||||
if !ok || chunk == nil {
|
||||
return
|
||||
}
|
||||
logs.CtxInfof(ctx, "hanlder event:%v,err:%v", conv.DebugJsonToStr(chunk), chunk.Err)
|
||||
if chunk.Err != nil {
|
||||
if errors.Is(chunk.Err, io.EOF) {
|
||||
return
|
||||
}
|
||||
c.handlerErr(ctx, chunk.Err, sw)
|
||||
return
|
||||
}
|
||||
|
||||
switch chunk.EventType {
|
||||
case message.MessageTypeFunctionCall:
|
||||
err = c.handlerFunctionCall(ctx, chunk, sw, rtDependence)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
case message.MessageTypeToolResponse:
|
||||
err = c.handlerTooResponse(ctx, chunk, sw, rtDependence)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
case message.MessageTypeKnowledge:
|
||||
err = c.handlerKnowledge(ctx, chunk, sw, rtDependence)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
case message.MessageTypeAnswer:
|
||||
fullContent := bytes.NewBuffer([]byte{})
|
||||
reasoningContent := bytes.NewBuffer([]byte{})
|
||||
|
||||
var preMsg *msgEntity.Message
|
||||
var usage *msgEntity.UsageExt
|
||||
var createPreMsg = true
|
||||
var isToolCalls = false
|
||||
|
||||
for {
|
||||
streamMsg, receErr := chunk.ModelAnswer.Recv()
|
||||
|
||||
if receErr != nil {
|
||||
if errors.Is(receErr, io.EOF) {
|
||||
|
||||
if isToolCalls && reasoningContent.String() == "" {
|
||||
break
|
||||
}
|
||||
|
||||
finalAnswer := c.buildSendMsg(ctx, preMsg, false, rtDependence)
|
||||
finalAnswer.Content = fullContent.String()
|
||||
finalAnswer.ReasoningContent = ptr.Of(reasoningContent.String())
|
||||
hfErr := c.handlerFinalAnswer(ctx, finalAnswer, sw, usage, rtDependence)
|
||||
if hfErr != nil {
|
||||
err = hfErr
|
||||
return
|
||||
}
|
||||
finishErr := c.handlerFinalAnswerFinish(ctx, sw, rtDependence)
|
||||
if finishErr != nil {
|
||||
err = finishErr
|
||||
return
|
||||
}
|
||||
break
|
||||
}
|
||||
err = receErr
|
||||
return
|
||||
}
|
||||
|
||||
if streamMsg != nil && len(streamMsg.ToolCalls) > 0 {
|
||||
isToolCalls = true
|
||||
}
|
||||
|
||||
if streamMsg != nil && streamMsg.ResponseMeta != nil {
|
||||
usage = c.handlerUsage(streamMsg.ResponseMeta)
|
||||
}
|
||||
|
||||
if streamMsg != nil && len(streamMsg.ReasoningContent) == 0 && len(streamMsg.Content) == 0 {
|
||||
continue
|
||||
}
|
||||
if createPreMsg && (len(streamMsg.ReasoningContent) > 0 || len(streamMsg.Content) > 0) {
|
||||
preMsg, err = c.handlerPreAnswer(ctx, rtDependence)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
createPreMsg = false
|
||||
}
|
||||
|
||||
sendMsg := c.buildSendMsg(ctx, preMsg, false, rtDependence)
|
||||
reasoningContent.WriteString(streamMsg.ReasoningContent)
|
||||
sendMsg.ReasoningContent = ptr.Of(streamMsg.ReasoningContent)
|
||||
|
||||
fullContent.WriteString(streamMsg.Content)
|
||||
sendMsg.Content = streamMsg.Content
|
||||
|
||||
c.runEvent.SendMsgEvent(entity.RunEventMessageDelta, sendMsg, sw)
|
||||
}
|
||||
|
||||
case message.MessageTypeFlowUp:
|
||||
err = c.handlerSuggest(ctx, chunk, sw, rtDependence)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
case message.MessageTypeInterrupt:
|
||||
err = c.handlerInterrupt(ctx, chunk, sw, rtDependence)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *runImpl) handlerInterrupt(ctx context.Context, chunk *entity.AgentRespEvent, sw *schema.StreamWriter[*entity.AgentRunResponse], rtDependence *runtimeDependence) error {
|
||||
interruptData, cType, err := c.parseInterruptData(ctx, chunk.Interrupt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
preMsg, err := c.handlerPreAnswer(ctx, rtDependence)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
deltaAnswer := &entity.ChunkMessageItem{
|
||||
ID: preMsg.ID,
|
||||
ConversationID: preMsg.ConversationID,
|
||||
SectionID: preMsg.SectionID,
|
||||
RunID: preMsg.RunID,
|
||||
AgentID: preMsg.AgentID,
|
||||
Role: entity.RoleType(preMsg.Role),
|
||||
Content: interruptData,
|
||||
MessageType: preMsg.MessageType,
|
||||
ContentType: cType,
|
||||
ReplyID: preMsg.RunID,
|
||||
Ext: preMsg.Ext,
|
||||
IsFinish: false,
|
||||
}
|
||||
|
||||
c.runEvent.SendMsgEvent(entity.RunEventMessageDelta, deltaAnswer, sw)
|
||||
finalAnswer := deepcopy.Copy(deltaAnswer).(*entity.ChunkMessageItem)
|
||||
|
||||
err = c.handlerFinalAnswer(ctx, finalAnswer, sw, nil, rtDependence)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = c.handlerInterruptVerbose(ctx, chunk, sw, rtDependence)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = c.handlerFinalAnswerFinish(ctx, sw, rtDependence)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *runImpl) parseInterruptData(_ context.Context, interruptData *singleagent.InterruptInfo) (string, message.ContentType, error) {
|
||||
|
||||
type msg struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
ContentType string `json:"content_type"`
|
||||
Content any `json:"content"` // either optionContent or string
|
||||
ID string `json:"id,omitempty"`
|
||||
}
|
||||
|
||||
defaultContentType := message.ContentTypeText
|
||||
switch interruptData.InterruptType {
|
||||
case singleagent.InterruptEventType_OauthPlugin:
|
||||
data := interruptData.AllToolInterruptData[interruptData.ToolCallID].ToolNeedOAuth.Message
|
||||
return data, defaultContentType, nil
|
||||
case singleagent.InterruptEventType_Question:
|
||||
var iData map[string][]*msg
|
||||
err := json.Unmarshal([]byte(interruptData.AllWfInterruptData[interruptData.ToolCallID].InterruptData), &iData)
|
||||
if err != nil {
|
||||
return "", defaultContentType, err
|
||||
}
|
||||
if len(iData["messages"]) == 0 {
|
||||
return "", defaultContentType, errorx.New(errno.ErrInterruptDataEmpty)
|
||||
}
|
||||
interruptMsg := iData["messages"][0]
|
||||
|
||||
if interruptMsg.ContentType == "text" {
|
||||
return interruptMsg.Content.(string), defaultContentType, nil
|
||||
} else if interruptMsg.ContentType == "option" || interruptMsg.ContentType == "form_schema" {
|
||||
iMarshalData, err := json.Marshal(interruptMsg)
|
||||
if err != nil {
|
||||
return "", defaultContentType, err
|
||||
}
|
||||
return string(iMarshalData), message.ContentTypeCard, nil
|
||||
}
|
||||
case singleagent.InterruptEventType_InputNode:
|
||||
data := interruptData.AllWfInterruptData[interruptData.ToolCallID].InterruptData
|
||||
return data, message.ContentTypeCard, nil
|
||||
case singleagent.InterruptEventType_WorkflowLLM:
|
||||
toolInterruptEvent := interruptData.AllWfInterruptData[interruptData.ToolCallID].ToolInterruptEvent
|
||||
data := toolInterruptEvent.InterruptData
|
||||
if singleagent.InterruptEventType(toolInterruptEvent.EventType) == singleagent.InterruptEventType_InputNode {
|
||||
return data, message.ContentTypeCard, nil
|
||||
}
|
||||
if singleagent.InterruptEventType(toolInterruptEvent.EventType) == singleagent.InterruptEventType_Question {
|
||||
var iData map[string][]*msg
|
||||
err := json.Unmarshal([]byte(data), &iData)
|
||||
if err != nil {
|
||||
return "", defaultContentType, err
|
||||
}
|
||||
if len(iData["messages"]) == 0 {
|
||||
return "", defaultContentType, errorx.New(errno.ErrInterruptDataEmpty)
|
||||
}
|
||||
interruptMsg := iData["messages"][0]
|
||||
|
||||
if interruptMsg.ContentType == "text" {
|
||||
return interruptMsg.Content.(string), defaultContentType, nil
|
||||
} else if interruptMsg.ContentType == "option" || interruptMsg.ContentType == "form_schema" {
|
||||
iMarshalData, err := json.Marshal(interruptMsg)
|
||||
if err != nil {
|
||||
return "", defaultContentType, err
|
||||
}
|
||||
return string(iMarshalData), message.ContentTypeCard, nil
|
||||
}
|
||||
}
|
||||
return "", defaultContentType, errorx.New(errno.ErrUnknowInterruptType)
|
||||
|
||||
}
|
||||
return "", defaultContentType, errorx.New(errno.ErrUnknowInterruptType)
|
||||
}
|
||||
|
||||
func (c *runImpl) handlerUsage(meta *schema.ResponseMeta) *msgEntity.UsageExt {
|
||||
if meta == nil || meta.Usage == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &msgEntity.UsageExt{
|
||||
TotalCount: int64(meta.Usage.TotalTokens),
|
||||
InputTokens: int64(meta.Usage.PromptTokens),
|
||||
OutputTokens: int64(meta.Usage.CompletionTokens),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *runImpl) handlerErr(_ context.Context, err error, sw *schema.StreamWriter[*entity.AgentRunResponse]) {
|
||||
c.runEvent.SendErrEvent(entity.RunEventError, sw, &entity.RunError{
|
||||
Code: errno.ErrConversationAgentRunError,
|
||||
Msg: errorx.ErrorWithoutStack(err),
|
||||
})
|
||||
}
|
||||
|
||||
func (c *runImpl) handlerPreAnswer(ctx context.Context, rtDependence *runtimeDependence) (*msgEntity.Message, error) {
|
||||
arm := rtDependence.runMeta
|
||||
msgMeta := &msgEntity.Message{
|
||||
ConversationID: arm.ConversationID,
|
||||
RunID: rtDependence.runID,
|
||||
AgentID: arm.AgentID,
|
||||
SectionID: arm.SectionID,
|
||||
UserID: arm.UserID,
|
||||
Role: schema.Assistant,
|
||||
MessageType: message.MessageTypeAnswer,
|
||||
ContentType: message.ContentTypeText,
|
||||
Ext: arm.Ext,
|
||||
}
|
||||
|
||||
if arm.Ext == nil {
|
||||
msgMeta.Ext = map[string]string{}
|
||||
}
|
||||
|
||||
botStateExt := c.buildBotStateExt(arm)
|
||||
bseString, err := json.Marshal(botStateExt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, ok := msgMeta.Ext[string(msgEntity.MessageExtKeyBotState)]; !ok {
|
||||
msgMeta.Ext[string(msgEntity.MessageExtKeyBotState)] = string(bseString)
|
||||
}
|
||||
|
||||
msgMeta.Ext = arm.Ext
|
||||
return crossmessage.DefaultSVC().Create(ctx, msgMeta)
|
||||
}
|
||||
|
||||
func (c *runImpl) handlerFinalAnswer(ctx context.Context, msg *entity.ChunkMessageItem, sw *schema.StreamWriter[*entity.AgentRunResponse], usage *msgEntity.UsageExt, rtDependence *runtimeDependence) error {
|
||||
|
||||
if len(msg.Content) == 0 && len(ptr.From(msg.ReasoningContent)) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
msg.IsFinish = true
|
||||
|
||||
if msg.Ext == nil {
|
||||
msg.Ext = map[string]string{}
|
||||
}
|
||||
if usage != nil {
|
||||
msg.Ext[string(msgEntity.MessageExtKeyToken)] = strconv.FormatInt(usage.TotalCount, 10)
|
||||
msg.Ext[string(msgEntity.MessageExtKeyInputTokens)] = strconv.FormatInt(usage.InputTokens, 10)
|
||||
msg.Ext[string(msgEntity.MessageExtKeyOutputTokens)] = strconv.FormatInt(usage.OutputTokens, 10)
|
||||
|
||||
rtDependence.usage = &agentrun.Usage{
|
||||
LlmPromptTokens: usage.InputTokens,
|
||||
LlmCompletionTokens: usage.OutputTokens,
|
||||
LlmTotalTokens: usage.TotalCount,
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := msg.Ext[string(msgEntity.MessageExtKeyTimeCost)]; !ok {
|
||||
msg.Ext[string(msgEntity.MessageExtKeyTimeCost)] = fmt.Sprintf("%.1f", float64(time.Since(rtDependence.startTime).Milliseconds())/1000.00)
|
||||
}
|
||||
|
||||
buildModelContent := &schema.Message{
|
||||
Role: schema.Assistant,
|
||||
Content: msg.Content,
|
||||
}
|
||||
|
||||
mc, err := json.Marshal(buildModelContent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
editMsg := &msgEntity.Message{
|
||||
ID: msg.ID,
|
||||
Content: msg.Content,
|
||||
ContentType: msg.ContentType,
|
||||
ModelContent: string(mc),
|
||||
ReasoningContent: ptr.From(msg.ReasoningContent),
|
||||
Ext: msg.Ext,
|
||||
}
|
||||
_, err = crossmessage.DefaultSVC().Edit(ctx, editMsg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.runEvent.SendMsgEvent(entity.RunEventMessageCompleted, msg, sw)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *runImpl) buildBotStateExt(arm *entity.AgentRunMeta) *msgEntity.BotStateExt {
|
||||
agentID := strconv.FormatInt(arm.AgentID, 10)
|
||||
botStateExt := &msgEntity.BotStateExt{
|
||||
AgentID: agentID,
|
||||
AgentName: arm.Name,
|
||||
Awaiting: agentID,
|
||||
BotID: agentID,
|
||||
}
|
||||
|
||||
return botStateExt
|
||||
}
|
||||
|
||||
func (c *runImpl) handlerFunctionCall(ctx context.Context, chunk *entity.AgentRespEvent, sw *schema.StreamWriter[*entity.AgentRunResponse], rtDependence *runtimeDependence) error {
|
||||
cm := c.buildAgentMessage2Create(ctx, chunk, message.MessageTypeFunctionCall, rtDependence)
|
||||
|
||||
cmData, err := crossmessage.DefaultSVC().Create(ctx, cm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sendMsg := c.buildSendMsg(ctx, cmData, true, rtDependence)
|
||||
|
||||
c.runEvent.SendMsgEvent(entity.RunEventMessageCompleted, sendMsg, sw)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *runImpl) handlerAckMessage(_ context.Context, input *msgEntity.Message, sw *schema.StreamWriter[*entity.AgentRunResponse]) error {
|
||||
sendMsg := &entity.ChunkMessageItem{
|
||||
ID: input.ID,
|
||||
ConversationID: input.ConversationID,
|
||||
SectionID: input.SectionID,
|
||||
AgentID: input.AgentID,
|
||||
Role: entity.RoleType(input.Role),
|
||||
MessageType: message.MessageTypeAck,
|
||||
ReplyID: input.ID,
|
||||
Content: input.Content,
|
||||
ContentType: message.ContentTypeText,
|
||||
IsFinish: true,
|
||||
}
|
||||
|
||||
c.runEvent.SendMsgEvent(entity.RunEventAck, sendMsg, sw)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *runImpl) handlerTooResponse(ctx context.Context, chunk *entity.AgentRespEvent, sw *schema.StreamWriter[*entity.AgentRunResponse], rtDependence *runtimeDependence) error {
|
||||
cm := c.buildAgentMessage2Create(ctx, chunk, message.MessageTypeToolResponse, rtDependence)
|
||||
cmData, err := crossmessage.DefaultSVC().Create(ctx, cm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sendMsg := c.buildSendMsg(ctx, cmData, true, rtDependence)
|
||||
|
||||
c.runEvent.SendMsgEvent(entity.RunEventMessageCompleted, sendMsg, sw)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *runImpl) handlerSuggest(ctx context.Context, chunk *entity.AgentRespEvent, sw *schema.StreamWriter[*entity.AgentRunResponse], rtDependence *runtimeDependence) error {
|
||||
cm := c.buildAgentMessage2Create(ctx, chunk, message.MessageTypeFlowUp, rtDependence)
|
||||
|
||||
cmData, err := crossmessage.DefaultSVC().Create(ctx, cm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sendMsg := c.buildSendMsg(ctx, cmData, true, rtDependence)
|
||||
|
||||
c.runEvent.SendMsgEvent(entity.RunEventMessageCompleted, sendMsg, sw)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *runImpl) handlerKnowledge(ctx context.Context, chunk *entity.AgentRespEvent, sw *schema.StreamWriter[*entity.AgentRunResponse], rtDependence *runtimeDependence) error {
|
||||
cm := c.buildAgentMessage2Create(ctx, chunk, message.MessageTypeKnowledge, rtDependence)
|
||||
cmData, err := crossmessage.DefaultSVC().Create(ctx, cm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sendMsg := c.buildSendMsg(ctx, cmData, true, rtDependence)
|
||||
|
||||
c.runEvent.SendMsgEvent(entity.RunEventMessageCompleted, sendMsg, sw)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *runImpl) buildKnowledge(_ context.Context, arm *entity.AgentRunMeta, chunk *entity.AgentRespEvent) *msgEntity.VerboseInfo {
|
||||
var recallDatas []msgEntity.RecallDataInfo
|
||||
for _, kOne := range chunk.Knowledge {
|
||||
recallDatas = append(recallDatas, msgEntity.RecallDataInfo{
|
||||
Slice: kOne.Content,
|
||||
Meta: msgEntity.MetaInfo{
|
||||
Dataset: msgEntity.DatasetInfo{
|
||||
ID: kOne.MetaData["dataset_id"].(string),
|
||||
Name: kOne.MetaData["dataset_name"].(string),
|
||||
},
|
||||
Document: msgEntity.DocumentInfo{
|
||||
ID: kOne.MetaData["document_id"].(string),
|
||||
Name: kOne.MetaData["document_name"].(string),
|
||||
},
|
||||
},
|
||||
Score: kOne.Score(),
|
||||
})
|
||||
}
|
||||
|
||||
verboseData := &msgEntity.VerboseData{
|
||||
Chunks: recallDatas,
|
||||
OriReq: "",
|
||||
StatusCode: 0,
|
||||
}
|
||||
data, err := json.Marshal(verboseData)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
knowledgeInfo := &msgEntity.VerboseInfo{
|
||||
MessageType: string(entity.MessageSubTypeKnowledgeCall),
|
||||
Data: string(data),
|
||||
}
|
||||
return knowledgeInfo
|
||||
}
|
||||
|
||||
func (c *runImpl) handlerFinalAnswerFinish(ctx context.Context, sw *schema.StreamWriter[*entity.AgentRunResponse], rtDependence *runtimeDependence) error {
|
||||
cm := c.buildAgentMessage2Create(ctx, nil, message.MessageTypeVerbose, rtDependence)
|
||||
cmData, err := crossmessage.DefaultSVC().Create(ctx, cm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sendMsg := c.buildSendMsg(ctx, cmData, true, rtDependence)
|
||||
|
||||
c.runEvent.SendMsgEvent(entity.RunEventMessageCompleted, sendMsg, sw)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *runImpl) handlerInterruptVerbose(ctx context.Context, chunk *entity.AgentRespEvent, sw *schema.StreamWriter[*entity.AgentRunResponse], rtDependence *runtimeDependence) error {
|
||||
cm := c.buildAgentMessage2Create(ctx, chunk, message.MessageTypeInterrupt, rtDependence)
|
||||
cmData, err := crossmessage.DefaultSVC().Create(ctx, cm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sendMsg := c.buildSendMsg(ctx, cmData, true, rtDependence)
|
||||
|
||||
c.runEvent.SendMsgEvent(entity.RunEventMessageCompleted, sendMsg, sw)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *runImpl) buildSendMsg(_ context.Context, msg *msgEntity.Message, isFinish bool, rtDependence *runtimeDependence) *entity.ChunkMessageItem {
|
||||
|
||||
copyMap := make(map[string]string)
|
||||
for k, v := range msg.Ext {
|
||||
copyMap[k] = v
|
||||
}
|
||||
|
||||
return &entity.ChunkMessageItem{
|
||||
ID: msg.ID,
|
||||
ConversationID: msg.ConversationID,
|
||||
SectionID: msg.SectionID,
|
||||
AgentID: msg.AgentID,
|
||||
Content: msg.Content,
|
||||
Role: entity.RoleTypeAssistant,
|
||||
ContentType: msg.ContentType,
|
||||
MessageType: msg.MessageType,
|
||||
ReplyID: rtDependence.questionMsgID,
|
||||
Type: msg.MessageType,
|
||||
CreatedAt: msg.CreatedAt,
|
||||
UpdatedAt: msg.UpdatedAt,
|
||||
RunID: rtDependence.runID,
|
||||
Ext: copyMap,
|
||||
IsFinish: isFinish,
|
||||
ReasoningContent: ptr.Of(msg.ReasoningContent),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *runImpl) buildSendRunRecord(_ context.Context, runRecord *entity.RunRecordMeta, runStatus entity.RunStatus) *entity.ChunkRunItem {
|
||||
return &entity.ChunkRunItem{
|
||||
ID: runRecord.ID,
|
||||
ConversationID: runRecord.ConversationID,
|
||||
AgentID: runRecord.AgentID,
|
||||
SectionID: runRecord.SectionID,
|
||||
Status: runStatus,
|
||||
CreatedAt: runRecord.CreatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *runImpl) Delete(ctx context.Context, runID []int64) error {
|
||||
return c.RunRecordRepo.Delete(ctx, runID)
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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 agentrun
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAgentRun(t *testing.T) {
|
||||
// ctx := context.Background()
|
||||
//
|
||||
// mockDB, err := mysql.New()
|
||||
// assert.Nil(t, err)
|
||||
// cacheCli := redis.New()
|
||||
//
|
||||
// idGen, err := idgen.New(cacheCli)
|
||||
// ctrl := gomock.NewController(t)
|
||||
// idGen := mock.NewMockIDGenerator(ctrl)
|
||||
// // idGen.EXPECT().GenMultiIDs(gomock.Any(), 2).Return([]int64{time.Now().UnixMilli(), time.Now().Add(time.Second).UnixMilli()}, nil).AnyTimes()
|
||||
// idGen.EXPECT().GenID(gomock.Any()).Return(int64(time.Now().UnixMilli()), nil).AnyTimes()
|
||||
//
|
||||
// mockDBGen := orm.NewMockDB()
|
||||
// mockDBGen.AddTable(&model.RunRecord{})
|
||||
// mockDB, err := mockDBGen.DB()
|
||||
//
|
||||
// assert.NoError(t, err)
|
||||
// components := &Components{
|
||||
// DB: mockDB,
|
||||
// IDGen: idGen,
|
||||
// }
|
||||
//
|
||||
// imageInput := &entity.FileData{
|
||||
// Url: "https://xxxxx.xxxx/image",
|
||||
// Name: "test_img",
|
||||
// }
|
||||
// fileInput := &entity.FileData{
|
||||
// Url: "https://xxxxx.xxxx/file",
|
||||
// Name: "test_file",
|
||||
// }
|
||||
// content := []*entity.InputMetaData{
|
||||
// {
|
||||
// Type: entity.InputTypeText,
|
||||
// Text: "你是谁",
|
||||
// },
|
||||
// {
|
||||
// Type: entity.InputTypeImage,
|
||||
// FileData: []*entity.FileData{
|
||||
// imageInput,
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// Type: entity.InputTypeFile,
|
||||
// FileData: []*entity.FileData{
|
||||
// fileInput,
|
||||
// },
|
||||
// },
|
||||
// }
|
||||
// stream, err := NewService(components, nil).AgentRun(ctx, &entity.AgentRunMeta{
|
||||
// ConversationID: 7503546991712960512,
|
||||
// SpaceID: 666,
|
||||
// SectionID: 7503546991712976896,
|
||||
// UserID: 888,
|
||||
// AgentID: 7501996002144944128,
|
||||
// Content: content,
|
||||
// ContentType: entity.ContentTypeMulti,
|
||||
// })
|
||||
// assert.NoError(t, err)
|
||||
// t.Logf("------------stream: %+v; err:%v", stream, err)
|
||||
//
|
||||
// for {
|
||||
// chunk, errRecv := stream.Recv()
|
||||
// jsonStr, _ := json.Marshal(chunk)
|
||||
// fmt.Println(string(jsonStr))
|
||||
// if errRecv == io.EOF || chunk == nil || chunk.Event == entity.RunEventStreamDone {
|
||||
// break
|
||||
// }
|
||||
// if errRecv != nil {
|
||||
// assert.NoError(t, errRecv)
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
|
||||
// assert.NoError(t, err)
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user