/* * 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 singleagent import ( "context" "fmt" "strconv" "sync" "time" "github.com/coze-dev/coze-studio/backend/api/model/app/developer_api" "github.com/coze-dev/coze-studio/backend/api/model/app/intelligence/common" "github.com/coze-dev/coze-studio/backend/api/model/crossdomain/database" "github.com/coze-dev/coze-studio/backend/api/model/playground" "github.com/coze-dev/coze-studio/backend/application/base/ctxutil" "github.com/coze-dev/coze-studio/backend/domain/agent/singleagent/entity" search "github.com/coze-dev/coze-studio/backend/domain/search/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/lang/slices" "github.com/coze-dev/coze-studio/backend/pkg/logs" "github.com/coze-dev/coze-studio/backend/pkg/taskgroup" "github.com/coze-dev/coze-studio/backend/types/errno" ) func (s *SingleAgentApplicationService) PublishAgent(ctx context.Context, req *developer_api.PublishDraftBotRequest) (*developer_api.PublishDraftBotResponse, error) { draftAgent, err := s.ValidateAgentDraftAccess(ctx, req.BotID) if err != nil { return nil, err } version, err := s.getPublishAgentVersion(ctx, req) if err != nil { return nil, err } connectorIDs := make([]int64, 0, len(req.Connectors)) for v := range req.Connectors { var id int64 id, err = strconv.ParseInt(v, 10, 64) if err != nil { return nil, err } if !entity.PublishConnectorIDWhiteList[id] { return nil, errorx.New(errno.ErrAgentPermissionCode, errorx.KV("msg", fmt.Sprintf("connector %d not allowed", id))) } connectorIDs = append(connectorIDs, id) } p := &entity.SingleAgentPublish{ ConnectorIds: connectorIDs, Version: version, PublishID: req.GetPublishID(), PublishInfo: req.HistoryInfo, } publishFns := []publishFn{ publishAgentVariables, publishAgentPlugins, publishShortcutCommand, publishDatabase, } for _, pubFn := range publishFns { draftAgent, err = pubFn(ctx, s.appContext, p, draftAgent) if err != nil { return nil, err } } err = s.DomainSVC.SavePublishRecord(ctx, p, draftAgent) if err != nil { return nil, err } tasks := taskgroup.NewUninterruptibleTaskGroup(ctx, len(connectorIDs)) publishResult := make(map[string]*developer_api.ConnectorBindResult, len(connectorIDs)) lock := sync.Mutex{} for _, connectorID := range connectorIDs { tasks.Go(func() error { _, err = s.DomainSVC.CreateSingleAgent(ctx, connectorID, version, draftAgent) if err != nil { logs.CtxWarnf(ctx, "create single agent failed: %v, agentID: %d, connectorID: %d , version : %s", err, draftAgent.AgentID, connectorID, version) lock.Lock() publishResult[conv.Int64ToStr(connectorID)] = &developer_api.ConnectorBindResult{ PublishResultStatus: ptr.Of(developer_api.PublishResultStatus_Failed), } lock.Unlock() return err } // do other connector publish logic if need lock.Lock() publishResult[conv.Int64ToStr(connectorID)] = &developer_api.ConnectorBindResult{ PublishResultStatus: ptr.Of(developer_api.PublishResultStatus_Success), } lock.Unlock() return nil }) } _ = tasks.Wait() err = s.appContext.EventBus.PublishProject(ctx, &search.ProjectDomainEvent{ OpType: search.Updated, Project: &search.ProjectDocument{ ID: draftAgent.AgentID, HasPublished: ptr.Of(1), PublishTimeMS: ptr.Of(time.Now().UnixMilli()), Type: common.IntelligenceType_Bot, }, }) if err != nil { logs.CtxWarnf(ctx, "publish project event failed, agentID: %d, err : %v", draftAgent.AgentID, err) } return &developer_api.PublishDraftBotResponse{ Data: &developer_api.PublishDraftBotData{ CheckNotPass: false, PublishResult: publishResult, }, }, nil } func (s *SingleAgentApplicationService) getPublishAgentVersion(ctx context.Context, req *developer_api.PublishDraftBotRequest) (string, error) { version := req.GetCommitVersion() if version != "" { return version, nil } v, err := s.appContext.IDGen.GenID(ctx) if err != nil { return "", err } version = fmt.Sprintf("%v", v) return version, nil } func (s *SingleAgentApplicationService) GetAgentPopupInfo(ctx context.Context, req *playground.GetBotPopupInfoRequest) (*playground.GetBotPopupInfoResponse, error) { uid := ctxutil.MustGetUIDFromCtx(ctx) agentPopupCountInfo := make(map[playground.BotPopupType]int64, len(req.BotPopupTypes)) for _, agentPopupType := range req.BotPopupTypes { count, err := s.DomainSVC.GetAgentPopupCount(ctx, uid, req.GetBotID(), agentPopupType) if err != nil { return nil, err } agentPopupCountInfo[agentPopupType] = count } return &playground.GetBotPopupInfoResponse{ Data: &playground.BotPopupInfoData{ BotPopupCountInfo: agentPopupCountInfo, }, }, nil } func (s *SingleAgentApplicationService) UpdateAgentPopupInfo(ctx context.Context, req *playground.UpdateBotPopupInfoRequest) (*playground.UpdateBotPopupInfoResponse, error) { uid := ctxutil.MustGetUIDFromCtx(ctx) err := s.DomainSVC.IncrAgentPopupCount(ctx, uid, req.GetBotID(), req.GetBotPopupType()) if err != nil { return nil, err } return &playground.UpdateBotPopupInfoResponse{ Code: 0, Msg: "success", }, nil } func (s *SingleAgentApplicationService) GetPublishConnectorList(ctx context.Context, req *developer_api.PublishConnectorListRequest) (*developer_api.PublishConnectorListResponse, error) { data, err := s.DomainSVC.GetPublishConnectorList(ctx, req.BotID) if err != nil { return nil, err } return &developer_api.PublishConnectorListResponse{ PublishConnectorList: data.PublishConnectorList, Code: 0, Msg: "success", }, nil } type publishFn func(ctx context.Context, appContext *ServiceComponents, publishInfo *entity.SingleAgentPublish, agent *entity.SingleAgent) (*entity.SingleAgent, error) func publishAgentVariables(ctx context.Context, appContext *ServiceComponents, publishInfo *entity.SingleAgentPublish, agent *entity.SingleAgent) (*entity.SingleAgent, error) { draftAgent := agent if draftAgent.VariablesMetaID != nil || *draftAgent.VariablesMetaID == 0 { return draftAgent, nil } var newVariableMetaID int64 newVariableMetaID, err := appContext.VariablesDomainSVC.PublishMeta(ctx, *draftAgent.VariablesMetaID, publishInfo.Version) if err != nil { return nil, err } draftAgent.VariablesMetaID = ptr.Of(newVariableMetaID) return draftAgent, nil } func publishAgentPlugins(ctx context.Context, appContext *ServiceComponents, publishInfo *entity.SingleAgentPublish, agent *entity.SingleAgent) (*entity.SingleAgent, error) { err := appContext.PluginDomainSVC.PublishAgentTools(ctx, agent.AgentID, publishInfo.Version) if err != nil { return nil, err } return agent, nil } func publishShortcutCommand(ctx context.Context, appContext *ServiceComponents, publishInfo *entity.SingleAgentPublish, agent *entity.SingleAgent) (*entity.SingleAgent, error) { logs.CtxInfof(ctx, "publishShortcutCommand agentID: %d, shortcutCommand: %v", agent.AgentID, agent.ShortcutCommand) if agent.ShortcutCommand == nil || len(agent.ShortcutCommand) == 0 { return agent, nil } cmdIDs := slices.Transform(agent.ShortcutCommand, func(a string) int64 { return conv.StrToInt64D(a, 0) }) err := appContext.ShortcutCMDDomainSVC.PublishCMDs(ctx, agent.AgentID, cmdIDs) if err != nil { return nil, err } return agent, nil } func publishDatabase(ctx context.Context, appContext *ServiceComponents, publishInfo *entity.SingleAgentPublish, agent *entity.SingleAgent) (*entity.SingleAgent, error) { onlineResp, err := appContext.DatabaseDomainSVC.PublishDatabase(ctx, &database.PublishDatabaseRequest{AgentID: agent.AgentID}) if err != nil { return nil, err } agent.Database = onlineResp.OnlineDatabases return agent, nil }