// // Copyright 2026 The InfiniFlow Authors. All Rights Reserved. // // 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 service import ( "fmt" "ragflow/internal/common" "ragflow/internal/dao" "ragflow/internal/entity" ) // AgentService agent service type AgentService struct { canvasDAO *dao.UserCanvasDAO userTenantDAO *dao.UserTenantDAO userCanvasVersionDAO *dao.UserCanvasVersionDAO } // NewAgentService create agent service func NewAgentService() *AgentService { return &AgentService{ canvasDAO: dao.NewUserCanvasDAO(), userTenantDAO: dao.NewUserTenantDAO(), userCanvasVersionDAO: dao.NewUserCanvasVersionDAO(), } } // AgentItem is one entry in the list response. type AgentItem struct { ID string `json:"id"` Avatar *string `json:"avatar,omitempty"` Title *string `json:"title,omitempty"` Permission string `json:"permission"` CanvasType *string `json:"canvas_type,omitempty"` CanvasCategory string `json:"canvas_category"` CreateTime *int64 `json:"create_time,omitempty"` UpdateTime *int64 `json:"update_time,omitempty"` } // ListAgentsResponse is the response body for GET /api/v1/agents. type ListAgentsResponse struct { Canvas []*AgentItem `json:"canvas"` Total int64 `json:"total"` } func toAgentItem(c *entity.UserCanvas) *AgentItem { return &AgentItem{ ID: c.ID, Avatar: c.Avatar, Title: c.Title, Permission: c.Permission, CanvasType: c.CanvasType, CanvasCategory: c.CanvasCategory, CreateTime: c.CreateTime, UpdateTime: c.UpdateTime, } } // ListAgents returns agent canvases visible to userID. // Mirrors Python agent_api.list_agents — validates owner_ids against joined tenants, // then delegates to the DAO. func (s *AgentService) ListAgents( userID string, keywords string, page, pageSize int, orderby string, desc bool, ownerIDs []string, canvasCategory string, ) (*ListAgentsResponse, common.ErrorCode, error) { // Build the set of tenant IDs the user is authorised to query. tenantIDs, err := s.userTenantDAO.GetTenantIDsByUserID(userID) if err != nil { return nil, common.CodeServerError, fmt.Errorf("failed to get tenant IDs: %w", err) } authorised := make(map[string]struct{}, len(tenantIDs)+1) for _, id := range tenantIDs { authorised[id] = struct{}{} } authorised[userID] = struct{}{} var effectiveOwnerIDs []string if len(ownerIDs) > 0 { for _, id := range ownerIDs { if _, ok := authorised[id]; !ok { return nil, common.CodeOperatingError, fmt.Errorf("only authorized owner_ids can be queried") } } effectiveOwnerIDs = ownerIDs } else { effectiveOwnerIDs = make([]string, 0, len(authorised)) for id := range authorised { effectiveOwnerIDs = append(effectiveOwnerIDs, id) } } canvases, total, err := s.canvasDAO.ListByTenantIDs( effectiveOwnerIDs, userID, page, pageSize, orderby, desc, keywords, canvasCategory, ) if err != nil { return nil, common.CodeServerError, fmt.Errorf("failed to list agents: %w", err) } items := make([]*AgentItem, len(canvases)) for i, c := range canvases { items[i] = toAgentItem(c) } return &ListAgentsResponse{Canvas: items, Total: total}, common.CodeSuccess, nil } // CheckCanvasAccess checks if a user has access to a canvas. // Returns true if the user is the owner or has team-level permission. func (s *AgentService) CheckCanvasAccess(userID, canvasID string) (bool, error) { canvas, err := s.canvasDAO.GetByID(canvasID) if err != nil { return false, err } // Owner always has access if canvas.UserID == userID { return true, nil } // Non-owner: only team-level permission grants tenant access if canvas.Permission != string(entity.TenantPermissionTeam) { return false, nil } tenantIDs, err := s.userTenantDAO.GetTenantIDsByUserID(userID) if err != nil { return false, err } for _, tid := range tenantIDs { if canvas.UserID == tid { return true, nil } } return false, nil } // ListVersions returns all versions for an agent canvas, ordered by update_time DESC. func (s *AgentService) ListVersions(canvasID string) ([]*entity.UserCanvasVersion, error) { return s.userCanvasVersionDAO.ListByCanvasID(canvasID) } // GetVersion returns a specific version by ID, verifying it belongs to the given canvas. func (s *AgentService) GetVersion(canvasID, versionID string) (*entity.UserCanvasVersion, error) { version, err := s.userCanvasVersionDAO.GetByID(versionID) if err != nil { return nil, err } if version.UserCanvasID != canvasID { return nil, fmt.Errorf("version not found") } return version, nil }