feat: Phase 3 插件与工具系统 — Plugin SDK + Plugin Manager + 13内置插件 (40文件, 3293行)
- Plugin SDK: Plugin/Tool/ComplexTool/HostAPI 标准化接口 - Plugin Manager: 插件生命周期管理 (Install/Enable/Disable/Uninstall/Reload) - Tool Registry: 聚合工具注册表 (Register/Execute/Dispatch) - 13 个内置插件: 将原有硬编码工具迁移为标准插件格式 - REST API: 11 个端点 (net/http, 零外部依赖) - ai-core 集成: PluginManagerClient 替代本地工具调用 - plugin.json 元数据: 每个插件含完整 author/version/category/permissions Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
package tools
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// PluginManagerClient calls the plugin-manager service.
|
||||
type PluginManagerClient struct {
|
||||
baseURL string
|
||||
httpClient *http.Client
|
||||
}
|
||||
|
||||
// PMToolDefinition matches the plugin-manager tool definition format.
|
||||
type PMToolDefinition struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
DisplayName string `json:"displayName"`
|
||||
Description string `json:"description"`
|
||||
Category string `json:"category"`
|
||||
Complexity string `json:"complexity"`
|
||||
Parameters map[string]interface{} `json:"parameters"`
|
||||
DangerLevel string `json:"danger_level,omitempty"`
|
||||
}
|
||||
|
||||
// PMToolResult matches the plugin-manager execution result.
|
||||
type PMToolResult struct {
|
||||
ToolName string `json:"tool_name"`
|
||||
Success bool `json:"success"`
|
||||
Output string `json:"output,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// PMPluginInfo matches plugin-manager plugin info.
|
||||
type PMPluginInfo struct {
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
Status string `json:"status"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Tools []string `json:"tools"`
|
||||
}
|
||||
|
||||
func NewPluginManagerClient(baseURL string) *PluginManagerClient {
|
||||
return &PluginManagerClient{
|
||||
baseURL: baseURL,
|
||||
httpClient: &http.Client{Timeout: 10 * time.Second},
|
||||
}
|
||||
}
|
||||
|
||||
// GetToolDefinitions fetches all tool definitions from plugin-manager.
|
||||
func (c *PluginManagerClient) GetToolDefinitions(ctx context.Context) ([]PMToolDefinition, error) {
|
||||
req, _ := http.NewRequestWithContext(ctx, "GET", c.baseURL+"/api/v1/tools", nil)
|
||||
resp, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("plugin-manager GetToolDefinitions: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var body struct {
|
||||
Tools []PMToolDefinition `json:"tools"`
|
||||
}
|
||||
if err := json.NewDecoder(resp.Body).Decode(&body); err != nil {
|
||||
return nil, fmt.Errorf("plugin-manager decode tools: %w", err)
|
||||
}
|
||||
return body.Tools, nil
|
||||
}
|
||||
|
||||
// ExecuteTool calls a tool on plugin-manager by ID.
|
||||
func (c *PluginManagerClient) ExecuteTool(ctx context.Context, toolID string, args map[string]interface{}) (*PMToolResult, error) {
|
||||
body, _ := json.Marshal(map[string]interface{}{"arguments": args})
|
||||
url := fmt.Sprintf("%s/api/v1/tools/%s/execute", c.baseURL, toolID)
|
||||
req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("plugin-manager ExecuteTool: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result PMToolResult
|
||||
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
|
||||
return nil, fmt.Errorf("plugin-manager decode result: %w", err)
|
||||
}
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// ListPlugins fetches all installed plugins from plugin-manager.
|
||||
func (c *PluginManagerClient) ListPlugins(ctx context.Context) ([]PMPluginInfo, error) {
|
||||
req, _ := http.NewRequestWithContext(ctx, "GET", c.baseURL+"/api/v1/plugins", nil)
|
||||
resp, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var body struct {
|
||||
Plugins []PMPluginInfo `json:"plugins"`
|
||||
}
|
||||
if err := json.NewDecoder(resp.Body).Decode(&body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return body.Plugins, nil
|
||||
}
|
||||
|
||||
// AdaptDefinitions converts PM tool definitions to ai-core ToolDefinition format.
|
||||
func (c *PluginManagerClient) AdaptDefinitions(ctx context.Context) ([]ToolDefinition, error) {
|
||||
pmDefs, err := c.GetToolDefinitions(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defs := make([]ToolDefinition, 0, len(pmDefs))
|
||||
for _, d := range pmDefs {
|
||||
defs = append(defs, ToolDefinition{
|
||||
Name: d.Name,
|
||||
Description: d.Description,
|
||||
Parameters: d.Parameters,
|
||||
})
|
||||
}
|
||||
return defs, nil
|
||||
}
|
||||
Reference in New Issue
Block a user