feat: support conversation isolation (#2443)

Co-authored-by: yangyu.1 <tomasyu985@gmail.com>
This commit is contained in:
junwen-lee
2025-11-05 21:35:17 +08:00
committed by GitHub
parent 5d1276b2b8
commit acfce615ec
29 changed files with 405 additions and 95 deletions

View File

@ -1,4 +1,20 @@
// Code generated by thriftgo (0.4.2). DO NOT EDIT.
/*
* 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.
*/
// Code generated by thriftgo (0.4.1). DO NOT EDIT.
package conversation
@ -1033,6 +1049,7 @@ type ConversationData struct {
LastSectionID *int64 `thrift:"LastSectionID,6,optional" form:"last_section_id" json:"last_section_id,string,omitempty"`
AccountID *int64 `thrift:"AccountID,7,optional" form:"account_id" json:"account_id,omitempty"`
Name *string `thrift:"Name,8,optional" form:"name" json:"name,omitempty"`
UserID *string `thrift:"UserID,9,optional" form:"user_id" json:"user_id,omitempty"`
}
func NewConversationData() *ConversationData {
@ -1099,6 +1116,15 @@ func (p *ConversationData) GetName() (v string) {
return *p.Name
}
var ConversationData_UserID_DEFAULT string
func (p *ConversationData) GetUserID() (v string) {
if !p.IsSetUserID() {
return ConversationData_UserID_DEFAULT
}
return *p.UserID
}
var fieldIDToName_ConversationData = map[int16]string{
1: "Id",
2: "CreatedAt",
@ -1108,6 +1134,7 @@ var fieldIDToName_ConversationData = map[int16]string{
6: "LastSectionID",
7: "AccountID",
8: "Name",
9: "UserID",
}
func (p *ConversationData) IsSetCreatorID() bool {
@ -1130,6 +1157,10 @@ func (p *ConversationData) IsSetName() bool {
return p.Name != nil
}
func (p *ConversationData) IsSetUserID() bool {
return p.UserID != nil
}
func (p *ConversationData) Read(iprot thrift.TProtocol) (err error) {
var fieldTypeId thrift.TType
var fieldId int16
@ -1212,6 +1243,14 @@ func (p *ConversationData) Read(iprot thrift.TProtocol) (err error) {
} else if err = iprot.Skip(fieldTypeId); err != nil {
goto SkipFieldError
}
case 9:
if fieldTypeId == thrift.STRING {
if err = p.ReadField9(iprot); err != nil {
goto ReadFieldError
}
} else if err = iprot.Skip(fieldTypeId); err != nil {
goto SkipFieldError
}
default:
if err = iprot.Skip(fieldTypeId); err != nil {
goto SkipFieldError
@ -1347,6 +1386,17 @@ func (p *ConversationData) ReadField8(iprot thrift.TProtocol) error {
p.Name = _field
return nil
}
func (p *ConversationData) ReadField9(iprot thrift.TProtocol) error {
var _field *string
if v, err := iprot.ReadString(); err != nil {
return err
} else {
_field = &v
}
p.UserID = _field
return nil
}
func (p *ConversationData) Write(oprot thrift.TProtocol) (err error) {
var fieldId int16
@ -1386,6 +1436,10 @@ func (p *ConversationData) Write(oprot thrift.TProtocol) (err error) {
fieldId = 8
goto WriteFieldError
}
if err = p.writeField9(oprot); err != nil {
fieldId = 9
goto WriteFieldError
}
}
if err = oprot.WriteFieldStop(); err != nil {
goto WriteFieldStopError
@ -1553,6 +1607,24 @@ WriteFieldBeginError:
WriteFieldEndError:
return thrift.PrependError(fmt.Sprintf("%T write field 8 end error: ", p), err)
}
func (p *ConversationData) writeField9(oprot thrift.TProtocol) (err error) {
if p.IsSetUserID() {
if err = oprot.WriteFieldBegin("UserID", thrift.STRING, 9); err != nil {
goto WriteFieldBeginError
}
if err := oprot.WriteString(*p.UserID); err != nil {
return err
}
if err = oprot.WriteFieldEnd(); err != nil {
goto WriteFieldEndError
}
}
return nil
WriteFieldBeginError:
return thrift.PrependError(fmt.Sprintf("%T write field 9 begin error: ", p), err)
WriteFieldEndError:
return thrift.PrependError(fmt.Sprintf("%T write field 9 end error: ", p), err)
}
func (p *ConversationData) String() string {
if p == nil {
@ -1567,6 +1639,7 @@ type CreateConversationRequest struct {
MetaData map[string]string `thrift:"MetaData,1,optional" form:"meta_data" json:"meta_data,omitempty"`
BotId *int64 `thrift:"BotId,3,optional" form:"bot_id" json:"bot_id,string,omitempty"`
ConnectorId *int64 `thrift:"ConnectorId,4,optional" form:"connector_id" json:"connector_id,string,omitempty"`
UserID *string `thrift:"user_id,50,optional" form:"user_id" json:"user_id,omitempty"`
}
func NewCreateConversationRequest() *CreateConversationRequest {
@ -1603,10 +1676,20 @@ func (p *CreateConversationRequest) GetConnectorId() (v int64) {
return *p.ConnectorId
}
var CreateConversationRequest_UserID_DEFAULT string
func (p *CreateConversationRequest) GetUserID() (v string) {
if !p.IsSetUserID() {
return CreateConversationRequest_UserID_DEFAULT
}
return *p.UserID
}
var fieldIDToName_CreateConversationRequest = map[int16]string{
1: "MetaData",
3: "BotId",
4: "ConnectorId",
50: "user_id",
}
func (p *CreateConversationRequest) IsSetMetaData() bool {
@ -1621,6 +1704,10 @@ func (p *CreateConversationRequest) IsSetConnectorId() bool {
return p.ConnectorId != nil
}
func (p *CreateConversationRequest) IsSetUserID() bool {
return p.UserID != nil
}
func (p *CreateConversationRequest) Read(iprot thrift.TProtocol) (err error) {
var fieldTypeId thrift.TType
var fieldId int16
@ -1663,6 +1750,14 @@ func (p *CreateConversationRequest) Read(iprot thrift.TProtocol) (err error) {
} else if err = iprot.Skip(fieldTypeId); err != nil {
goto SkipFieldError
}
case 50:
if fieldTypeId == thrift.STRING {
if err = p.ReadField50(iprot); err != nil {
goto ReadFieldError
}
} else if err = iprot.Skip(fieldTypeId); err != nil {
goto SkipFieldError
}
default:
if err = iprot.Skip(fieldTypeId); err != nil {
goto SkipFieldError
@ -1743,6 +1838,17 @@ func (p *CreateConversationRequest) ReadField4(iprot thrift.TProtocol) error {
p.ConnectorId = _field
return nil
}
func (p *CreateConversationRequest) ReadField50(iprot thrift.TProtocol) error {
var _field *string
if v, err := iprot.ReadString(); err != nil {
return err
} else {
_field = &v
}
p.UserID = _field
return nil
}
func (p *CreateConversationRequest) Write(oprot thrift.TProtocol) (err error) {
var fieldId int16
@ -1762,6 +1868,10 @@ func (p *CreateConversationRequest) Write(oprot thrift.TProtocol) (err error) {
fieldId = 4
goto WriteFieldError
}
if err = p.writeField50(oprot); err != nil {
fieldId = 50
goto WriteFieldError
}
}
if err = oprot.WriteFieldStop(); err != nil {
goto WriteFieldStopError
@ -1845,6 +1955,24 @@ WriteFieldBeginError:
WriteFieldEndError:
return thrift.PrependError(fmt.Sprintf("%T write field 4 end error: ", p), err)
}
func (p *CreateConversationRequest) writeField50(oprot thrift.TProtocol) (err error) {
if p.IsSetUserID() {
if err = oprot.WriteFieldBegin("user_id", thrift.STRING, 50); err != nil {
goto WriteFieldBeginError
}
if err := oprot.WriteString(*p.UserID); err != nil {
return err
}
if err = oprot.WriteFieldEnd(); err != nil {
goto WriteFieldEndError
}
}
return nil
WriteFieldBeginError:
return thrift.PrependError(fmt.Sprintf("%T write field 50 begin error: ", p), err)
WriteFieldEndError:
return thrift.PrependError(fmt.Sprintf("%T write field 50 end error: ", p), err)
}
func (p *CreateConversationRequest) String() string {
if p == nil {
@ -2771,6 +2899,7 @@ type ListConversationsApiRequest struct {
SortField string `thrift:"sort_field,4" json:"sort_field" query:"sort_field"`
BotID int64 `thrift:"bot_id,5,required" json:"bot_id,string,required" query:"bot_id,required"`
ConnectorID *int64 `thrift:"connector_id,6,optional" json:"connector_id,string,omitempty" query:"connector_id"`
UserID *string `thrift:"user_id,50,optional" json:"user_id,omitempty" query:"user_id"`
Base *base.Base `thrift:"Base,255" form:"Base" json:"Base" query:"Base"`
}
@ -2810,6 +2939,15 @@ func (p *ListConversationsApiRequest) GetConnectorID() (v int64) {
return *p.ConnectorID
}
var ListConversationsApiRequest_UserID_DEFAULT string
func (p *ListConversationsApiRequest) GetUserID() (v string) {
if !p.IsSetUserID() {
return ListConversationsApiRequest_UserID_DEFAULT
}
return *p.UserID
}
var ListConversationsApiRequest_Base_DEFAULT *base.Base
func (p *ListConversationsApiRequest) GetBase() (v *base.Base) {
@ -2826,6 +2964,7 @@ var fieldIDToName_ListConversationsApiRequest = map[int16]string{
4: "sort_field",
5: "bot_id",
6: "connector_id",
50: "user_id",
255: "Base",
}
@ -2833,6 +2972,10 @@ func (p *ListConversationsApiRequest) IsSetConnectorID() bool {
return p.ConnectorID != nil
}
func (p *ListConversationsApiRequest) IsSetUserID() bool {
return p.UserID != nil
}
func (p *ListConversationsApiRequest) IsSetBase() bool {
return p.Base != nil
}
@ -2905,6 +3048,14 @@ func (p *ListConversationsApiRequest) Read(iprot thrift.TProtocol) (err error) {
} else if err = iprot.Skip(fieldTypeId); err != nil {
goto SkipFieldError
}
case 50:
if fieldTypeId == thrift.STRING {
if err = p.ReadField50(iprot); err != nil {
goto ReadFieldError
}
} else if err = iprot.Skip(fieldTypeId); err != nil {
goto SkipFieldError
}
case 255:
if fieldTypeId == thrift.STRUCT {
if err = p.ReadField255(iprot); err != nil {
@ -3014,6 +3165,17 @@ func (p *ListConversationsApiRequest) ReadField6(iprot thrift.TProtocol) error {
p.ConnectorID = _field
return nil
}
func (p *ListConversationsApiRequest) ReadField50(iprot thrift.TProtocol) error {
var _field *string
if v, err := iprot.ReadString(); err != nil {
return err
} else {
_field = &v
}
p.UserID = _field
return nil
}
func (p *ListConversationsApiRequest) ReadField255(iprot thrift.TProtocol) error {
_field := base.NewBase()
if err := _field.Read(iprot); err != nil {
@ -3053,6 +3215,10 @@ func (p *ListConversationsApiRequest) Write(oprot thrift.TProtocol) (err error)
fieldId = 6
goto WriteFieldError
}
if err = p.writeField50(oprot); err != nil {
fieldId = 50
goto WriteFieldError
}
if err = p.writeField255(oprot); err != nil {
fieldId = 255
goto WriteFieldError
@ -3173,6 +3339,24 @@ WriteFieldBeginError:
WriteFieldEndError:
return thrift.PrependError(fmt.Sprintf("%T write field 6 end error: ", p), err)
}
func (p *ListConversationsApiRequest) writeField50(oprot thrift.TProtocol) (err error) {
if p.IsSetUserID() {
if err = oprot.WriteFieldBegin("user_id", thrift.STRING, 50); err != nil {
goto WriteFieldBeginError
}
if err := oprot.WriteString(*p.UserID); err != nil {
return err
}
if err = oprot.WriteFieldEnd(); err != nil {
goto WriteFieldEndError
}
}
return nil
WriteFieldBeginError:
return thrift.PrependError(fmt.Sprintf("%T write field 50 begin error: ", p), err)
WriteFieldEndError:
return thrift.PrependError(fmt.Sprintf("%T write field 50 end error: ", p), err)
}
func (p *ListConversationsApiRequest) writeField255(oprot thrift.TProtocol) (err error) {
if err = oprot.WriteFieldBegin("Base", thrift.STRUCT, 255); err != nil {
goto WriteFieldBeginError

View File

@ -1,4 +1,20 @@
// Code generated by thriftgo (0.4.2). DO NOT EDIT.
/*
* 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.
*/
// Code generated by thriftgo (0.4.1). DO NOT EDIT.
package conversation

View File

@ -269,7 +269,7 @@ func (c *ConversationApplicationService) checkConversation(ctx context.Context,
conData, err := c.ConversationDomainSVC.Create(ctx, &convEntity.CreateMeta{
AgentID: ar.BotID,
UserID: userID,
CreatorID: userID,
Scene: ptr.From(ar.Scene),
ConnectorID: consts.CozeConnectorID,
})

View File

@ -83,7 +83,7 @@ func (c *ConversationApplicationService) ClearHistory(ctx context.Context, req *
// create new conversation
convRes, err := c.ConversationDomainSVC.Create(ctx, &entity.CreateMeta{
AgentID: currentRes.AgentID,
UserID: currentRes.CreatorID,
CreatorID: currentRes.CreatorID,
Scene: currentRes.Scene,
ConnectorID: consts.CozeConnectorID,
})
@ -126,7 +126,7 @@ func (c *ConversationApplicationService) CreateSection(ctx context.Context, conv
func (c *ConversationApplicationService) CreateConversation(ctx context.Context, req *conversation.CreateConversationRequest) (*conversation.CreateConversationResponse, error) {
resp := new(conversation.CreateConversationResponse)
apiKeyInfo := ctxutil.GetApiAuthFromCtx(ctx)
userID := apiKeyInfo.UserID
creatorID := apiKeyInfo.UserID
connectorID := req.GetConnectorId()
agentID := req.GetBotId()
if connectorID != consts.WebSDKConnectorID {
@ -135,7 +135,8 @@ func (c *ConversationApplicationService) CreateConversation(ctx context.Context,
conversationData, err := c.ConversationDomainSVC.Create(ctx, &entity.CreateMeta{
AgentID: agentID,
UserID: userID,
UserID: req.UserID,
CreatorID: creatorID,
ConnectorID: connectorID,
Scene: common.Scene_SceneOpenApi,
Ext: parseMetaData(req.MetaData),
@ -149,6 +150,7 @@ func (c *ConversationApplicationService) CreateConversation(ctx context.Context,
ConnectorID: &conversationData.ConnectorID,
CreatedAt: conversationData.CreatedAt / 1000,
MetaData: parseExt(conversationData.Ext),
UserID: conversationData.UserID,
}
return resp, nil
}
@ -191,7 +193,7 @@ func (c *ConversationApplicationService) ListConversation(ctx context.Context, r
}
conversationDOList, hasMore, err := c.ConversationDomainSVC.List(ctx, &entity.ListMeta{
UserID: userID,
CreatorID: userID,
AgentID: req.GetBotID(),
ConnectorID: connectorID,
Scene: common.Scene_SceneOpenApi,
@ -203,6 +205,7 @@ func (c *ConversationApplicationService) ListConversation(ctx context.Context, r
}
return nil
}(),
UserID: req.UserID,
})
if err != nil {
return resp, err
@ -215,6 +218,7 @@ func (c *ConversationApplicationService) ListConversation(ctx context.Context, r
CreatedAt: conv.CreatedAt / 1000,
Name: ptr.Of(conv.Name),
MetaData: parseExt(conv.Ext),
UserID: conv.UserID,
}
})

View File

@ -141,7 +141,7 @@ func (c *ConversationApplicationService) getCurrentConversation(ctx context.Cont
// create conversation
ccNew, err := c.ConversationDomainSVC.Create(ctx, &convEntity.CreateMeta{
AgentID: agentID,
UserID: userID,
CreatorID: userID,
Scene: scene,
ConnectorID: ptr.From(connectorID),
})

View File

@ -91,7 +91,7 @@ func (a *OpenapiAgentRunApplication) OpenapiAgentRun(ctx context.Context, sseSen
return nil
}
func (a *OpenapiAgentRunApplication) checkConversation(ctx context.Context, ar *run.ChatV3Request, userID int64, connectorID int64) (*convEntity.Conversation, error) {
func (a *OpenapiAgentRunApplication) checkConversation(ctx context.Context, ar *run.ChatV3Request, creatorID int64, connectorID int64) (*convEntity.Conversation, error) {
var conversationData *convEntity.Conversation
if ptr.From(ar.ConversationID) > 0 {
conData, err := ConversationSVC.ConversationDomainSVC.GetByID(ctx, ptr.From(ar.ConversationID))
@ -105,9 +105,10 @@ func (a *OpenapiAgentRunApplication) checkConversation(ctx context.Context, ar *
conData, err := ConversationSVC.ConversationDomainSVC.Create(ctx, &convEntity.CreateMeta{
AgentID: ar.BotID,
UserID: userID,
CreatorID: creatorID,
ConnectorID: connectorID,
Scene: common.Scene_SceneOpenApi,
UserID: ptr.Of(ar.User),
})
if err != nil {
return nil, err
@ -120,7 +121,7 @@ func (a *OpenapiAgentRunApplication) checkConversation(ctx context.Context, ar *
ar.ConversationID = ptr.Of(conversationData.ID)
}
if conversationData.CreatorID != userID {
if conversationData.CreatorID != creatorID {
return nil, errorx.New(errno.ErrConversationPermissionCode, errorx.KV("msg", "user not match"))
}

View File

@ -283,10 +283,11 @@ func TestOpenapiAgentRun_CreateNewConversation(t *testing.T) {
ID: 22222,
CreatorID: 12345,
SectionID: 98765,
UserID: ptr.Of("test-user"),
}
mockConversation.EXPECT().Create(ctx, gomock.Any()).DoAndReturn(func(ctx context.Context, meta *convEntity.CreateMeta) (*convEntity.Conversation, error) {
assert.Equal(t, int64(67890), meta.AgentID)
assert.Equal(t, int64(12345), meta.UserID)
assert.Equal(t, "test-user", ptr.From(meta.UserID))
assert.Equal(t, common.Scene_SceneOpenApi, meta.Scene)
return mockConv, nil
})

View File

@ -550,6 +550,12 @@ func (w *ApplicationService) OpenAPIChatFlowRun(ctx context.Context, req *workfl
apiKeyInfo = ctxutil.GetApiAuthFromCtx(ctx)
userID = apiKeyInfo.UserID
connectorID int64
runtimeUserID = func() *string {
if uID, ok := req.Ext["user_id"]; ok {
return ptr.Of(uID)
}
return nil
}()
)
if len(req.GetConnectorID()) == 0 {
connectorID = ternary.IFElse(isDebug, consts.CozeConnectorID, apiKeyInfo.ConnectorID)
@ -679,7 +685,12 @@ func (w *ApplicationService) OpenAPIChatFlowRun(ctx context.Context, req *workfl
Operator: userID,
Mode: ternary.IFElse(isDebug, workflowModel.ExecuteModeDebug, workflowModel.ExecuteModeRelease),
ConnectorID: connectorID,
ConnectorUID: strconv.FormatInt(userID, 10),
ConnectorUID: func() string {
if runtimeUserID != nil {
return *runtimeUserID
}
return strconv.FormatInt(userID, 10)
}(),
BizType: workflowModel.BizTypeWorkflow,
})
@ -712,7 +723,12 @@ func (w *ApplicationService) OpenAPIChatFlowRun(ctx context.Context, req *workfl
AppID: appID,
AgentID: agentID,
ConnectorID: connectorID,
ConnectorUID: strconv.FormatInt(userID, 10),
ConnectorUID: func() string {
if runtimeUserID != nil {
return *runtimeUserID
}
return strconv.FormatInt(userID, 10)
}(),
TaskType: workflowModel.TaskTypeForeground,
SyncPattern: workflowModel.SyncPatternStream,
InputFailFast: true,

View File

@ -1513,6 +1513,12 @@ func (w *ApplicationService) OpenAPIStreamRun(ctx context.Context, req *workflow
apiKeyInfo := ctxutil.GetApiAuthFromCtx(ctx)
userID := apiKeyInfo.UserID
runtimeUserID := func() *string {
if uID, ok := req.Ext["user_id"]; ok {
return ptr.Of(uID)
}
return nil
}()
parameters := make(map[string]any)
if req.Parameters != nil {
err := sonic.UnmarshalString(*req.Parameters, &parameters)
@ -1565,7 +1571,12 @@ func (w *ApplicationService) OpenAPIStreamRun(ctx context.Context, req *workflow
AppID: appID,
AgentID: agentID,
ConnectorID: connectorID,
ConnectorUID: strconv.FormatInt(userID, 10),
ConnectorUID: func() string {
if runtimeUserID != nil {
return *runtimeUserID
}
return strconv.FormatInt(userID, 10)
}(),
TaskType: workflowModel.TaskTypeForeground,
SyncPattern: workflowModel.SyncPatternStream,
InputFailFast: true,
@ -1624,6 +1635,12 @@ func (w *ApplicationService) OpenAPIStreamResume(ctx context.Context, req *workf
apiKeyInfo := ctxutil.GetApiAuthFromCtx(ctx)
userID := apiKeyInfo.UserID
runtimeUserID := func() *string {
if uID, ok := req.Ext["user_id"]; ok {
return ptr.Of(uID)
}
return nil
}()
checkResult, err := crosspermission.DefaultSVC().CheckAuthz(ctx, &permission.CheckAuthzData{
OperatorID: userID,
@ -1653,7 +1670,12 @@ func (w *ApplicationService) OpenAPIStreamResume(ctx context.Context, req *workf
Operator: userID,
Mode: workflowModel.ExecuteModeRelease,
ConnectorID: connectorID,
ConnectorUID: strconv.FormatInt(userID, 10),
ConnectorUID: func() string {
if runtimeUserID != nil {
return *runtimeUserID
}
return strconv.FormatInt(userID, 10)
}(),
BizType: workflowModel.BizTypeWorkflow,
})
if err != nil {
@ -1680,6 +1702,12 @@ func (w *ApplicationService) OpenAPIRun(ctx context.Context, req *workflow.OpenA
apiKeyInfo := ctxutil.GetApiAuthFromCtx(ctx)
userID := apiKeyInfo.UserID
runtimeUserID := func() *string {
if uID, ok := req.Ext["user_id"]; ok {
return ptr.Of(uID)
}
return nil
}()
parameters := make(map[string]any)
if req.Parameters != nil {
@ -1733,7 +1761,12 @@ func (w *ApplicationService) OpenAPIRun(ctx context.Context, req *workflow.OpenA
AppID: appID,
AgentID: agentID,
ConnectorID: connectorID,
ConnectorUID: strconv.FormatInt(userID, 10),
ConnectorUID: func() string {
if runtimeUserID != nil {
return *runtimeUserID
}
return strconv.FormatInt(userID, 10)
}(),
InputFailFast: true,
BizType: workflowModel.BizTypeWorkflow,
}

View File

@ -47,6 +47,7 @@ type Conversation struct {
AgentID int64 `json:"agent_id"`
ConnectorID int64 `json:"connector_id"`
CreatorID int64 `json:"creator_id"`
UserID *string `json:"user_id"`
Scene common.Scene `json:"scene"`
Status ConversationStatus `json:"status"`
Ext string `json:"ext"`

View File

@ -26,7 +26,8 @@ type Conversation = model.Conversation
type CreateMeta struct {
Name string `json:"name"`
AgentID int64 `json:"agent_id"`
UserID int64 `json:"user_id"`
UserID *string `json:"user_id"`
CreatorID int64 `json:"creator_id"`
ConnectorID int64 `json:"connector_id"`
Scene common.Scene `json:"scene"`
Ext string `json:"ext"`
@ -44,7 +45,8 @@ type NewConversationCtxResponse struct {
type GetCurrent = model.GetCurrent
type ListMeta struct {
UserID int64 `json:"user_id"`
CreatorID int64 `json:"creator_id"`
UserID *string `json:"user_id"`
ConnectorID int64 `json:"connector_id"`
Scene common.Scene `json:"scene"`
AgentID int64 `json:"agent_id"`

View File

@ -145,12 +145,16 @@ func (dao *ConversationDAO) List(ctx context.Context, listMeta *entity.ListMeta)
var hasMore bool
do := dao.query.Conversation.WithContext(ctx).Debug()
do = do.Where(dao.query.Conversation.CreatorID.Eq(listMeta.UserID)).
do = do.Where(dao.query.Conversation.CreatorID.Eq(listMeta.CreatorID)).
Where(dao.query.Conversation.AgentID.Eq(listMeta.AgentID)).
Where(dao.query.Conversation.Scene.Eq(int32(listMeta.Scene))).
Where(dao.query.Conversation.ConnectorID.Eq(listMeta.ConnectorID)).
Where(dao.query.Conversation.Status.Eq(int32(conversation.ConversationStatusNormal)))
if listMeta.UserID != nil {
do = do.Where(dao.query.Conversation.UserID.Eq(ptr.From(listMeta.UserID)))
}
do = do.Offset((listMeta.Page - 1) * listMeta.Limit)
if listMeta.Limit > 0 {
@ -188,6 +192,7 @@ func (dao *ConversationDAO) conversationDO2PO(ctx context.Context, conversation
SectionID: conversation.SectionID,
ConnectorID: conversation.ConnectorID,
AgentID: conversation.AgentID,
UserID: ptr.From(conversation.UserID),
CreatorID: conversation.CreatorID,
Scene: int32(conversation.Scene),
Status: int32(conversation.Status),
@ -211,6 +216,7 @@ func (dao *ConversationDAO) conversationPO2DO(ctx context.Context, c *model.Conv
CreatedAt: c.CreatedAt,
UpdatedAt: c.UpdatedAt,
Name: c.Name,
UserID: ptr.Of(c.UserID),
}
}
@ -228,6 +234,7 @@ func (dao *ConversationDAO) conversationBatchPO2DO(ctx context.Context, conversa
CreatedAt: c.CreatedAt,
UpdatedAt: c.UpdatedAt,
Name: c.Name,
UserID: ptr.Of(c.UserID),
}
})
}

View File

@ -25,7 +25,6 @@ const TableNameConversation = "conversation"
// Conversation conversation info record
type Conversation struct {
ID int64 `gorm:"column:id;primaryKey;autoIncrement:true;comment:id" json:"id"` // id
Name string `gorm:"column:name;not null;comment:conversation name" json:"name"` // conversation name
ConnectorID int64 `gorm:"column:connector_id;not null;comment:Publish Connector ID" json:"connector_id"` // Publish Connector ID
AgentID int64 `gorm:"column:agent_id;not null;comment:agent_id" json:"agent_id"` // agent_id
Scene int32 `gorm:"column:scene;not null;comment:conversation scene" json:"scene"` // conversation scene
@ -35,6 +34,8 @@ type Conversation struct {
Status int32 `gorm:"column:status;not null;default:1;comment:status: 1-normal 2-deleted" json:"status"` // status: 1-normal 2-deleted
CreatedAt int64 `gorm:"column:created_at;not null;autoCreateTime:milli;comment:Create Time in Milliseconds" json:"created_at"` // Create Time in Milliseconds
UpdatedAt int64 `gorm:"column:updated_at;not null;autoUpdateTime:milli;comment:Update Time in Milliseconds" json:"updated_at"` // Update Time in Milliseconds
Name string `gorm:"column:name;not null;comment:conversation name" json:"name"` // conversation name
UserID string `gorm:"column:user_id;not null;comment:user id" json:"user_id"` // user id
}
// TableName Conversation's table name

View File

@ -44,7 +44,6 @@ func newConversation(db *gorm.DB, opts ...gen.DOOption) conversation {
tableName := _conversation.conversationDo.TableName()
_conversation.ALL = field.NewAsterisk(tableName)
_conversation.ID = field.NewInt64(tableName, "id")
_conversation.Name = field.NewString(tableName, "name")
_conversation.ConnectorID = field.NewInt64(tableName, "connector_id")
_conversation.AgentID = field.NewInt64(tableName, "agent_id")
_conversation.Scene = field.NewInt32(tableName, "scene")
@ -54,6 +53,8 @@ func newConversation(db *gorm.DB, opts ...gen.DOOption) conversation {
_conversation.Status = field.NewInt32(tableName, "status")
_conversation.CreatedAt = field.NewInt64(tableName, "created_at")
_conversation.UpdatedAt = field.NewInt64(tableName, "updated_at")
_conversation.Name = field.NewString(tableName, "name")
_conversation.UserID = field.NewString(tableName, "user_id")
_conversation.fillFieldMap()
@ -66,7 +67,6 @@ type conversation struct {
ALL field.Asterisk
ID field.Int64 // id
Name field.String // conversation name
ConnectorID field.Int64 // Publish Connector ID
AgentID field.Int64 // agent_id
Scene field.Int32 // conversation scene
@ -76,6 +76,8 @@ type conversation struct {
Status field.Int32 // status: 1-normal 2-deleted
CreatedAt field.Int64 // Create Time in Milliseconds
UpdatedAt field.Int64 // Update Time in Milliseconds
Name field.String // conversation name
UserID field.String // user id
fieldMap map[string]field.Expr
}
@ -93,7 +95,6 @@ func (c conversation) As(alias string) *conversation {
func (c *conversation) updateTableName(table string) *conversation {
c.ALL = field.NewAsterisk(table)
c.ID = field.NewInt64(table, "id")
c.Name = field.NewString(table, "name")
c.ConnectorID = field.NewInt64(table, "connector_id")
c.AgentID = field.NewInt64(table, "agent_id")
c.Scene = field.NewInt32(table, "scene")
@ -103,6 +104,8 @@ func (c *conversation) updateTableName(table string) *conversation {
c.Status = field.NewInt32(table, "status")
c.CreatedAt = field.NewInt64(table, "created_at")
c.UpdatedAt = field.NewInt64(table, "updated_at")
c.Name = field.NewString(table, "name")
c.UserID = field.NewString(table, "user_id")
c.fillFieldMap()
@ -119,9 +122,8 @@ func (c *conversation) GetFieldByName(fieldName string) (field.OrderExpr, bool)
}
func (c *conversation) fillFieldMap() {
c.fieldMap = make(map[string]field.Expr, 11)
c.fieldMap = make(map[string]field.Expr, 12)
c.fieldMap["id"] = c.ID
c.fieldMap["name"] = c.Name
c.fieldMap["connector_id"] = c.ConnectorID
c.fieldMap["agent_id"] = c.AgentID
c.fieldMap["scene"] = c.Scene
@ -131,6 +133,8 @@ func (c *conversation) fillFieldMap() {
c.fieldMap["status"] = c.Status
c.fieldMap["created_at"] = c.CreatedAt
c.fieldMap["updated_at"] = c.UpdatedAt
c.fieldMap["name"] = c.Name
c.fieldMap["user_id"] = c.UserID
}
func (c conversation) clone(db *gorm.DB) conversation {

View File

@ -40,11 +40,12 @@ func (c *conversationImpl) Create(ctx context.Context, req *entity.CreateMeta) (
var resp *entity.Conversation
doData := &entity.Conversation{
CreatorID: req.UserID,
CreatorID: req.CreatorID,
AgentID: req.AgentID,
Scene: req.Scene,
ConnectorID: req.ConnectorID,
Ext: req.Ext,
UserID: req.UserID,
}
resp, err := c.ConversationRepo.Create(ctx, doData)

View File

@ -57,7 +57,7 @@ func TestCreateConversation(t *testing.T) {
createData, err := NewService(components).Create(ctx, &entity.CreateMeta{
AgentID: 100000,
UserID: 222222,
CreatorID: 222222,
ConnectorID: 100001,
Scene: common.Scene_Playground,
Ext: "debug ext9999",

View File

@ -79,7 +79,7 @@ func (c *CreateConversation) Invoke(ctx context.Context, input map[string]any) (
conversationIDGenerator = workflow.ConversationIDGenerator(func(ctx context.Context, appID int64, userID, connectorID int64) (*conventity.Conversation, error) {
return crossconversation.DefaultSVC().CreateConversation(ctx, &conventity.CreateMeta{
AgentID: appID,
UserID: userID,
CreatorID: userID,
ConnectorID: connectorID,
Scene: common.Scene_SceneWorkflow,
})

View File

@ -89,7 +89,7 @@ func (c *CreateMessage) getConversationIDByName(ctx context.Context, env vo.Env,
conversationIDGenerator := workflow.ConversationIDGenerator(func(ctx context.Context, appID int64, userID, connectorID int64) (*conventity.Conversation, error) {
return crossconversation.DefaultSVC().CreateConversation(ctx, &conventity.CreateMeta{
AgentID: appID,
UserID: userID,
CreatorID: userID,
ConnectorID: connectorID,
Scene: common.Scene_SceneWorkflow,
})

View File

@ -421,9 +421,10 @@ func getExecUserID(ctx context.Context) string {
if execCtx == nil {
panic(fmt.Errorf("unable to get exe context"))
}
if execCtx.RootCtx.ExeCfg.AgentID != nil {
if execCtx.RootCtx.ExeCfg.ConnectorUID != "" {
return execCtx.RootCtx.ExeCfg.ConnectorUID
}
uIDStr := strconv.FormatInt(execCtx.RootCtx.ExeCfg.Operator, 10)
return uIDStr
}

View File

@ -43,7 +43,7 @@ func ExecutePlugin(ctx context.Context, input map[string]any, pe *vo.PluginEntit
}
var uID string
if cfg.AgentID != nil {
if cfg.ConnectorUID != "" {
uID = cfg.ConnectorUID
} else {
uID = conv.Int64ToStr(cfg.Operator)

View File

@ -363,7 +363,7 @@ func (c *conversationImpl) GetOrCreateConversation(ctx context.Context, env vo.E
conversationIDGenerator := workflow.ConversationIDGenerator(func(ctx context.Context, bizID int64, userID, connectorID int64) (*conventity.Conversation, error) {
return crossconversation.DefaultSVC().CreateConversation(ctx, &conventity.CreateMeta{
AgentID: bizID,
UserID: userID,
CreatorID: userID,
ConnectorID: connectorID,
Scene: common.Scene_SceneWorkflow,
})
@ -408,7 +408,7 @@ func (c *conversationImpl) UpdateConversation(ctx context.Context, env vo.Env, a
if existed {
conv, err := crossconversation.DefaultSVC().CreateConversation(ctx, &conventity.CreateMeta{
AgentID: appID,
UserID: userID,
CreatorID: userID,
ConnectorID: connectorID,
Scene: common.Scene_SceneWorkflow,
})
@ -436,7 +436,7 @@ func (c *conversationImpl) UpdateConversation(ctx context.Context, env vo.Env, a
conv, err := crossconversation.DefaultSVC().CreateConversation(ctx, &conventity.CreateMeta{
AgentID: appID,
UserID: userID,
CreatorID: userID,
ConnectorID: connectorID,
Scene: common.Scene_SceneWorkflow,
})

View File

@ -0,0 +1,2 @@
-- Modify "conversation" table
ALTER TABLE `opencoze`.`conversation` ADD COLUMN `user_id` varchar(255) NOT NULL DEFAULT "" COMMENT "user id with runtime";

View File

@ -1,4 +1,4 @@
h1:fRmZ1DqaVJmbdXGbkWkJGCWbHy3gTQxkg6KRhpDBYeA=
h1:rpu8MnfqHOGZgvvh8p2w3lMlIMNBL/6X1UZ6ng6v4oM=
20250703095335_initial.sql h1:/joaeUTMhXqAEc0KwsSve5+bYM0qPOp+9OizJtsRc+U=
20250703115304_update.sql h1:cbYo6Q6Lh96hB4hu5KW2Nn/Mr0VDpg7a1WPgpIb1SOc=
20250704040445_update.sql h1:QWmoPY//oQ+GFZwET9w/oAWa8mM0KVaD5G8Yiu9bMqY=
@ -15,3 +15,4 @@ h1:fRmZ1DqaVJmbdXGbkWkJGCWbHy3gTQxkg6KRhpDBYeA=
20251015103940_update.sql h1:ivE+qtF4q3bMFroid1yMSO465s5Oiey/5VvQ7GWnBlY=
20251016034721_update.sql h1:BLXpKpa3LBVyiuMDGSwud6WwM+34jQQvn11ytYZCbrw=
20251024091145_update.sql h1:WH/24vD2bYK6udS7GRO1Enzw81SjCpRcbOSkzUhiMrk=
20251028085526_update.sql h1:Nv8QPX8ctvnQmDeA8z4kr1ltTEULErgD7FxpMHyzYCg=

View File

@ -1016,12 +1016,6 @@ table "conversation" {
comment = "id"
auto_increment = true
}
column "name" {
null = true
type = varchar(255)
default = ""
comment = "conversation name"
}
column "connector_id" {
null = false
type = bigint
@ -1080,6 +1074,18 @@ table "conversation" {
unsigned = true
comment = "Update Time in Milliseconds"
}
column "name" {
null = true
type = varchar(255)
default = ""
comment = "conversation name"
}
column "user_id" {
null = false
type = varchar(255)
default = ""
comment = "user id with runtime"
}
primary_key {
columns = [column.id]
}
@ -3256,6 +3262,11 @@ table "single_agent_draft" {
type = json
comment = "Agent Database Base Configuration"
}
column "shortcut_command" {
null = true
type = json
comment = "shortcut command"
}
column "bot_mode" {
null = false
type = tinyint
@ -3267,11 +3278,6 @@ table "single_agent_draft" {
type = text
comment = "chatflow layout info"
}
column "shortcut_command" {
null = true
type = json
comment = "shortcut command"
}
primary_key {
columns = [column.id]
}
@ -3428,17 +3434,6 @@ table "single_agent_version" {
unsigned = true
comment = "Create Time in Milliseconds"
}
column "bot_mode" {
null = false
type = tinyint
default = 0
comment = "bot mode,0:single mode 2:chatflow mode"
}
column "layout_info" {
null = true
type = text
comment = "chatflow layout info"
}
column "updated_at" {
null = false
type = bigint
@ -3523,6 +3518,17 @@ table "single_agent_version" {
type = json
comment = "shortcut command"
}
column "bot_mode" {
null = false
type = tinyint
default = 0
comment = "bot mode,0:single mode 2:chatflow mode"
}
column "layout_info" {
null = true
type = text
comment = "chatflow layout info"
}
primary_key {
columns = [column.id]
}

View File

@ -34,6 +34,7 @@ import {
type SortedConversationItem,
} from '@/types/conversations';
import { Layout } from '@/types/client';
import { useUserInfo } from '@/components/studio-open-chat/hooks';
import {
PcConversationItem,
@ -93,6 +94,8 @@ export const ConversationList = forwardRef<
})),
);
const userInfo = useUserInfo();
const conversationRef = useRef<ChatState['currentConversationInfo']>();
const [addLoading, setAddLoading] = useState(false);
const {
@ -114,6 +117,7 @@ export const ConversationList = forwardRef<
bot_id: botId,
// @ts-expect-error: 有这个属性,但是 openapi 没有暴露
connector_id: connectorId,
user_id: IS_OPEN_SOURCE ? userInfo?.id : undefined,
});
if (res?.id) {
conversationRef.current = {

View File

@ -27,11 +27,13 @@ import {
import { type ChatState } from '../store/store';
import { useChatAppProps, useChatAppStore } from '../store';
import { useUserInfo } from './use-user-info';
import { usePaginationRequest } from './use-pagination-request';
// 扩展ListConversationReq类型以满足PaginationParams约束
type ExtendedListConversationReq = ListConversationReq & {
sort_field: 'created_at' | 'updated_at';
user_id?: string; // 开源专有
[key: string]: unknown;
};
@ -76,6 +78,8 @@ export const useConversationList = (
})),
);
const userInfo = useUserInfo();
const { data, hasMore, loadMore, loading } = usePaginationRequest<
Conversation,
ExtendedListConversationReq
@ -99,6 +103,7 @@ export const useConversationList = (
bot_id: botId,
connector_id: connectorId,
sort_field: order,
user_id: IS_OPEN_SOURCE ? userInfo?.id : undefined,
},
pageSize,
initialPageNum,

View File

@ -20,6 +20,7 @@ import { type SceneConfig } from '@coze-common/chat-core';
import { OpenApiSource } from '@/types/open';
import { useChatAppProps } from '@/components/studio-open-chat/store';
import { useUserInfo } from '@/components/studio-open-chat/hooks';
import { type ChatProviderFunc } from '../type';
export const useClearHistoryAdapter = ({
@ -29,6 +30,7 @@ export const useClearHistoryAdapter = ({
}): SceneConfig => {
const { chatConfig } = useChatAppProps();
const refConnectorId = useRef('');
const userInfo = useUserInfo();
refConnectorId.current = chatConfig?.auth?.connectorId || '';
return useMemo(() => {
@ -60,7 +62,11 @@ export const useClearHistoryAdapter = ({
const botId = requestConfig.data.bot_id;
return {
...requestConfig,
data: { bot_id: botId, connector_id: refConnectorId.current },
data: {
bot_id: botId,
connector_id: refConnectorId.current,
user_id: IS_OPEN_SOURCE ? userInfo?.id : undefined,
},
};
},
],

View File

@ -17,7 +17,10 @@
import { useCallback } from 'react';
import { type ShortCutCommand } from '@coze-common/chat-area-plugins-chat-shortcuts';
import { type MixInitResponse } from '@coze-common/chat-area';
import {
type UserSenderInfo,
type MixInitResponse,
} from '@coze-common/chat-area';
import i18n from '@coze-arch/i18n/intl';
import { type BotInfo, type CozeAPI } from '@coze/api';
@ -134,12 +137,14 @@ const getConversationInfo = async ({
connectorId,
defaultHistoryMessage,
onDefaultHistoryClear,
userInfo,
}: GetRequestInfoProps & {
conversationId?: string;
sectionId?: string;
connectorId: string;
defaultHistoryMessage?: MixInitResponse['messageList'];
onDefaultHistoryClear?: () => void;
userInfo: UserSenderInfo | null;
}): Promise<
Pick<
MixInitResponse,
@ -161,6 +166,7 @@ const getConversationInfo = async ({
connector_id: connectorId,
page_num: 1,
page_size: 1,
user_id: IS_OPEN_SOURCE ? userInfo?.id : undefined,
},
)) as {
data: {
@ -185,6 +191,7 @@ const getConversationInfo = async ({
messages: historyMessage,
// @ts-expect-error: connector_id is not in the type
connector_id: connectorId,
user_id: IS_OPEN_SOURCE ? userInfo?.id : undefined,
},
{
headers: {
@ -204,6 +211,7 @@ const getConversationInfo = async ({
bot_id: botId,
// @ts-expect-error: connector_id is not in the type
connector_id: connectorId,
user_id: IS_OPEN_SOURCE ? userInfo?.id : undefined,
},
{
headers: {
@ -329,6 +337,7 @@ export const useRequestInit = () => {
connectorId,
onDefaultHistoryClear,
defaultHistoryMessage,
userInfo,
}),
]);
const prologue = (requestDataBotInfo.prologue || '').replaceAll(

View File

@ -37,12 +37,15 @@ struct ConversationData {
6: optional i64 LastSectionID (api.body="last_section_id", api.js_conv="true")
7: optional i64 AccountID (api.body = "account_id")
8: optional string Name (api.body = "name")
9: optional string UserID (api.body = "user_id")
}
struct CreateConversationRequest {
1: optional map<string,string> MetaData (api.body = "meta_data") //custom passthrough field
3: optional i64 BotId (api.body = "bot_id", api.js_conv="true")
4: optional i64 ConnectorId (api.body= "connector_id", api.js_conv="true")
50: optional string user_id (api.body="user_id")
}
struct CreateConversationResponse {
@ -78,6 +81,8 @@ struct ListConversationsApiRequest {
5 : required i64 bot_id (api.query = "bot_id", agw.key = "bot_id",api.js_conv="true")
6 : optional i64 connector_id (api.query = "connector_id", agw.key = "connector_id",api.js_conv="true")
50 : optional string user_id (api.query = "user_id", agw.key = "user_id")
255: base.Base Base
}