Files
ttstd-infinite-canvas/backend/internal/app/cloud_agent_pi_canvas_tools_registry.go

574 lines
17 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package app
import (
"context"
"fmt"
)
// 画布工具注册表:
// 1. 可扩展:新增工具只需注册,无需修改核心代码
// 2. 类型安全:每个工具有明确的参数和返回类型定义
// 3. 自动发现:工具定义自动生成 OpenAPI schema
// 4. 权限控制:每个工具可以配置权限和审批要求
// CanvasToolRegistry 画布工具注册表
type CanvasToolRegistry struct {
service *Service
tools map[string]*CanvasToolDefinition
}
// CanvasToolDefinition 画布工具定义
type CanvasToolDefinition struct {
Name string `json:"name"`
DisplayName string `json:"displayName"`
Description string `json:"description"`
Category string `json:"category"` // node/relationship/layout/search/bulk
Parameters map[string]any `json:"parameters"`
Schema map[string]any `json:"schema,omitempty"`
RequireApproval bool `json:"requireApproval"`
Permissions []string `json:"permissions"`
Examples []map[string]any `json:"examples,omitempty"`
Handler CanvasToolHandler `json:"-"`
}
// CanvasToolHandler 工具处理器
type CanvasToolHandler func(ctx context.Context, userID, runID string, args map[string]any) (any, error)
// NewCanvasToolRegistry 创建工具注册表
func (s *Service) NewCanvasToolRegistry() *CanvasToolRegistry {
registry := &CanvasToolRegistry{
service: s,
tools: make(map[string]*CanvasToolDefinition),
}
// 注册所有画布工具
registry.registerAllTools()
return registry
}
// registerAllTools 注册所有工具
func (r *CanvasToolRegistry) registerAllTools() {
// 节点操作工具
r.registerNodeTools()
// 关系操作工具
r.registerRelationshipTools()
// 布局工具
r.registerLayoutTools()
// 搜索工具
r.registerSearchTools()
// 批量操作工具
r.registerBulkTools()
// 快照工具
r.registerSnapshotTools()
}
// registerNodeTools 注册节点工具
func (r *CanvasToolRegistry) registerNodeTools() {
// 1. 创建节点
r.Register(&CanvasToolDefinition{
Name: "canvas_node_create",
DisplayName: "创建画布节点",
Description: "在画布上创建一个新节点。支持多种节点类型(text/markdown/code/image/diagram等),可以指定位置、大小、父节点等属性。",
Category: "node",
Parameters: map[string]any{
"type": "object",
"properties": map[string]any{
"type": map[string]any{
"type": "string",
"description": "节点类型:text/markdown/code/image/video/link/diagram/mindmap/task等",
"enum": []string{"text", "markdown", "code", "image", "video", "audio", "link", "diagram", "mindmap", "flowchart", "task", "note"},
},
"content": map[string]any{
"type": "string",
"description": "节点内容",
},
"position": map[string]any{
"type": "object",
"description": "节点位置 {x: number, y: number}",
"properties": map[string]any{
"x": map[string]any{"type": "number"},
"y": map[string]any{"type": "number"},
},
},
"size": map[string]any{
"type": "object",
"description": "节点尺寸 {width: number, height: number}",
"properties": map[string]any{
"width": map[string]any{"type": "number"},
"height": map[string]any{"type": "number"},
},
},
"parentId": map[string]any{
"type": "string",
"description": "父节点ID(可选)",
},
"metadata": map[string]any{
"type": "object",
"description": "额外元数据(可选)",
},
},
"required": []string{"type", "content"},
},
RequireApproval: false,
Permissions: []string{"canCreate"},
Examples: []map[string]any{
{
"type": "text",
"content": "这是一个文本节点",
"position": map[string]any{
"x": 100,
"y": 200,
},
},
{
"type": "task",
"content": "完成代码审查",
"parentId": "parent-node-id",
},
},
Handler: r.handleNodeCreate,
})
// 2. 读取节点
r.Register(&CanvasToolDefinition{
Name: "canvas_node_read",
DisplayName: "读取画布节点",
Description: "读取指定节点的完整信息,可以选择是否包含子节点。",
Category: "node",
Parameters: map[string]any{
"type": "object",
"properties": map[string]any{
"nodeId": map[string]any{
"type": "string",
"description": "节点ID",
},
"includeChildren": map[string]any{
"type": "boolean",
"description": "是否包含子节点(默认false)",
},
},
"required": []string{"nodeId"},
},
RequireApproval: false,
Permissions: []string{"canRead"},
Handler: r.handleNodeRead,
})
// 3. 更新节点
r.Register(&CanvasToolDefinition{
Name: "canvas_node_update",
DisplayName: "更新画布节点",
Description: "更新节点的内容、位置、尺寸或元数据。只需要提供要更新的字段。",
Category: "node",
Parameters: map[string]any{
"type": "object",
"properties": map[string]any{
"nodeId": map[string]any{
"type": "string",
"description": "节点ID",
},
"content": map[string]any{
"type": "string",
"description": "新的内容(可选)",
},
"position": map[string]any{
"type": "object",
"description": "新的位置(可选)",
},
"size": map[string]any{
"type": "object",
"description": "新的尺寸(可选)",
},
"metadata": map[string]any{
"type": "object",
"description": "新的元数据(可选)",
},
},
"required": []string{"nodeId"},
},
RequireApproval: false,
Permissions: []string{"canUpdate"},
Handler: r.handleNodeUpdate,
})
// 4. 删除节点
r.Register(&CanvasToolDefinition{
Name: "canvas_node_delete",
DisplayName: "删除画布节点",
Description: "删除指定节点。如果节点有子节点,可以选择是否同时删除。此操作需要用户审批。",
Category: "node",
Parameters: map[string]any{
"type": "object",
"properties": map[string]any{
"nodeId": map[string]any{
"type": "string",
"description": "节点ID",
},
"reason": map[string]any{
"type": "string",
"description": "删除原因",
},
"recursive": map[string]any{
"type": "boolean",
"description": "是否递归删除子节点(默认false)",
},
},
"required": []string{"nodeId", "reason"},
},
RequireApproval: true,
Permissions: []string{"canDelete"},
Handler: r.handleNodeDelete,
})
// 5. 移动节点
r.Register(&CanvasToolDefinition{
Name: "canvas_node_move",
DisplayName: "移动画布节点",
Description: "移动节点到新位置,或改变节点的父子关系。",
Category: "node",
Parameters: map[string]any{
"type": "object",
"properties": map[string]any{
"nodeId": map[string]any{
"type": "string",
"description": "节点ID",
},
"position": map[string]any{
"type": "object",
"description": "新位置 {x: number, y: number}(可选)",
},
"newParentId": map[string]any{
"type": "string",
"description": "新父节点ID(可选,null表示移到根层级)",
},
},
"required": []string{"nodeId"},
},
RequireApproval: false,
Permissions: []string{"canMove"},
Handler: r.handleNodeMove,
})
// 6. 复制节点
r.Register(&CanvasToolDefinition{
Name: "canvas_node_duplicate",
DisplayName: "复制画布节点",
Description: "复制一个节点,可以选择是否复制子节点。",
Category: "node",
Parameters: map[string]any{
"type": "object",
"properties": map[string]any{
"nodeId": map[string]any{
"type": "string",
"description": "要复制的节点ID",
},
"includeChildren": map[string]any{
"type": "boolean",
"description": "是否复制子节点(默认false)",
},
"position": map[string]any{
"type": "object",
"description": "新节点位置(可选)",
},
},
"required": []string{"nodeId"},
},
RequireApproval: false,
Permissions: []string{"canCreate"},
Handler: r.handleNodeDuplicate,
})
}
// registerRelationshipTools 注册关系工具
func (r *CanvasToolRegistry) registerRelationshipTools() {
// 1. 创建关系
r.Register(&CanvasToolDefinition{
Name: "canvas_relationship_create",
DisplayName: "创建节点关系",
Description: "在两个节点之间建立关系。支持多种关系类型(reference/dependency/flow/semantic)。",
Category: "relationship",
Parameters: map[string]any{
"type": "object",
"properties": map[string]any{
"fromNodeId": map[string]any{
"type": "string",
"description": "源节点ID",
},
"toNodeId": map[string]any{
"type": "string",
"description": "目标节点ID",
},
"type": map[string]any{
"type": "string",
"description": "关系类型",
"enum": []string{"reference", "dependency", "flow", "semantic", "custom"},
},
"bidirection": map[string]any{
"type": "boolean",
"description": "是否双向(默认false)",
},
"label": map[string]any{
"type": "string",
"description": "关系标签(可选)",
},
},
"required": []string{"fromNodeId", "toNodeId", "type"},
},
RequireApproval: false,
Permissions: []string{"canManageRelations"},
Handler: r.handleRelationshipCreate,
})
// 2. 删除关系
r.Register(&CanvasToolDefinition{
Name: "canvas_relationship_delete",
DisplayName: "删除节点关系",
Description: "删除两个节点之间的关系。",
Category: "relationship",
Parameters: map[string]any{
"type": "object",
"properties": map[string]any{
"fromNodeId": map[string]any{
"type": "string",
"description": "源节点ID",
},
"toNodeId": map[string]any{
"type": "string",
"description": "目标节点ID",
},
"type": map[string]any{
"type": "string",
"description": "关系类型(可选,不指定则删除所有关系)",
},
},
"required": []string{"fromNodeId", "toNodeId"},
},
RequireApproval: false,
Permissions: []string{"canManageRelations"},
Handler: r.handleRelationshipDelete,
})
}
// registerLayoutTools 注册布局工具
func (r *CanvasToolRegistry) registerLayoutTools() {
r.Register(&CanvasToolDefinition{
Name: "canvas_layout",
DisplayName: "画布自动布局",
Description: "对画布或指定节点组进行自动布局。支持多种布局算法(tree/grid/force/circular)。",
Category: "layout",
Parameters: map[string]any{
"type": "object",
"properties": map[string]any{
"algorithm": map[string]any{
"type": "string",
"description": "布局算法",
"enum": []string{"tree", "grid", "force", "circular", "hierarchical", "auto"},
},
"nodeIds": map[string]any{
"type": "array",
"description": "要布局的节点ID列表(可选,不指定则布局全部)",
"items": map[string]any{
"type": "string",
},
},
"direction": map[string]any{
"type": "string",
"description": "布局方向(tree/hierarchical算法适用)",
"enum": []string{"horizontal", "vertical"},
},
"spacing": map[string]any{
"type": "number",
"description": "节点间距(默认100)",
},
},
"required": []string{"algorithm"},
},
RequireApproval: false,
Permissions: []string{"canMove"},
Handler: r.handleLayout,
})
}
// registerSearchTools 注册搜索工具
func (r *CanvasToolRegistry) registerSearchTools() {
r.Register(&CanvasToolDefinition{
Name: "canvas_search",
DisplayName: "搜索画布节点",
Description: "根据关键词、类型、标签等条件搜索画布节点。支持全文搜索和过滤。",
Category: "search",
Parameters: map[string]any{
"type": "object",
"properties": map[string]any{
"query": map[string]any{
"type": "string",
"description": "搜索关键词(可选)",
},
"type": map[string]any{
"type": "string",
"description": "节点类型过滤(可选)",
},
"tags": map[string]any{
"type": "array",
"description": "标签过滤(可选)",
"items": map[string]any{
"type": "string",
},
},
"category": map[string]any{
"type": "string",
"description": "分类过滤(可选)",
},
"limit": map[string]any{
"type": "number",
"description": "返回结果数量限制(默认20)",
},
},
},
RequireApproval: false,
Permissions: []string{"canRead"},
Handler: r.handleSearch,
})
}
// registerBulkTools 注册批量工具
func (r *CanvasToolRegistry) registerBulkTools() {
r.Register(&CanvasToolDefinition{
Name: "canvas_bulk_operation",
DisplayName: "批量操作节点",
Description: "对多个节点执行批量操作(更新/删除/移动)。此操作需要用户审批。",
Category: "bulk",
Parameters: map[string]any{
"type": "object",
"properties": map[string]any{
"operation": map[string]any{
"type": "string",
"description": "操作类型",
"enum": []string{"update", "delete", "move", "tag"},
},
"nodeIds": map[string]any{
"type": "array",
"description": "节点ID列表",
"items": map[string]any{
"type": "string",
},
},
"params": map[string]any{
"type": "object",
"description": "操作参数",
},
"reason": map[string]any{
"type": "string",
"description": "操作原因",
},
},
"required": []string{"operation", "nodeIds", "reason"},
},
RequireApproval: true,
Permissions: []string{"canUpdate", "canDelete", "canMove"},
Handler: r.handleBulkOperation,
})
}
// registerSnapshotTools 注册快照工具
func (r *CanvasToolRegistry) registerSnapshotTools() {
r.Register(&CanvasToolDefinition{
Name: "canvas_snapshot",
DisplayName: "获取画布快照",
Description: "获取当前画布的结构化快照,包括节点、关系、布局分析、智能洞察等。",
Category: "snapshot",
Parameters: map[string]any{
"type": "object",
"properties": map[string]any{
"includeIntelligence": map[string]any{
"type": "boolean",
"description": "是否包含智能分析(默认true)",
},
"focusNodeIds": map[string]any{
"type": "array",
"description": "焦点节点ID列表(可选)",
"items": map[string]any{
"type": "string",
},
},
},
},
RequireApproval: false,
Permissions: []string{"canRead"},
Handler: r.handleSnapshot,
})
}
// Register 注册工具
func (r *CanvasToolRegistry) Register(tool *CanvasToolDefinition) {
if tool.Schema == nil {
tool.Schema = tool.Parameters
}
r.tools[tool.Name] = tool
}
// GetToolDefinition 获取工具定义
func (r *CanvasToolRegistry) GetToolDefinition(name string) (*CanvasToolDefinition, bool) {
tool, ok := r.tools[name]
return tool, ok
}
// GetAllToolDefinitions 获取所有工具定义
func (r *CanvasToolRegistry) GetAllToolDefinitions() []map[string]any {
result := make([]map[string]any, 0, len(r.tools))
for _, tool := range r.tools {
result = append(result, map[string]any{
"name": tool.Name,
"displayName": tool.DisplayName,
"description": tool.Description,
"category": tool.Category,
"parameters": tool.Parameters,
"requireApproval": tool.RequireApproval,
"permissions": tool.Permissions,
"examples": tool.Examples,
})
}
return result
}
// ExecuteTool 执行工具
func (r *CanvasToolRegistry) ExecuteTool(ctx context.Context, userID, runID, toolName string, args map[string]any) (any, error) {
tool, ok := r.GetToolDefinition(toolName)
if !ok {
return nil, fmt.Errorf("unknown tool: %s", toolName)
}
// 验证权限
if err := r.validatePermissions(userID, tool.Permissions); err != nil {
return nil, fmt.Errorf("permission denied: %w", err)
}
// 验证参数
if err := r.validateParameters(tool, args); err != nil {
return nil, fmt.Errorf("invalid parameters: %w", err)
}
// 检查是否需要审批
if tool.RequireApproval {
if approved, err := r.checkApproval(userID, runID, toolName, args); err != nil || !approved {
return map[string]any{
"requiresApproval": true,
"status": "pending_approval",
"message": "此操作需要用户审批",
}, nil
}
}
// 执行工具
return tool.Handler(ctx, userID, runID, args)
}
// 工具处理器实现将在下一个文件中