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,120 @@
|
||||
package iotquery
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/yourname/cyrene-ai/plugin-manager/internal/sdk"
|
||||
)
|
||||
|
||||
// IoTClient is the interface for IoT device access.
|
||||
type IoTClient interface {
|
||||
GetAllDevices(ctx context.Context) ([]sdk.IoTDeviceState, error)
|
||||
GetDevice(ctx context.Context, deviceID string) (*sdk.IoTDeviceState, error)
|
||||
}
|
||||
|
||||
type IoTQueryPlugin struct {
|
||||
sdk.BasePlugin
|
||||
iotClient IoTClient
|
||||
}
|
||||
|
||||
func NewIoTQueryPlugin(client IoTClient) *IoTQueryPlugin {
|
||||
return &IoTQueryPlugin{iotClient: client}
|
||||
}
|
||||
|
||||
func (p *IoTQueryPlugin) Metadata() sdk.PluginMetadata {
|
||||
return sdk.PluginMetadata{
|
||||
Name: "iot_query", DisplayName: "IoT Device Query", Version: "1.0.0",
|
||||
Description: "Query smart home device status (single device or all devices)",
|
||||
Category: "iot", Author: sdk.PluginAuthor{Name: "Cyrene Team"},
|
||||
}
|
||||
}
|
||||
|
||||
func (p *IoTQueryPlugin) Tools() []sdk.Tool { return []sdk.Tool{&IoTQueryTool{iotClient: p.iotClient}} }
|
||||
|
||||
type IoTQueryTool struct {
|
||||
sdk.BaseTool
|
||||
iotClient IoTClient
|
||||
}
|
||||
|
||||
func (t *IoTQueryTool) Definition() sdk.ToolDefinition {
|
||||
return sdk.ToolDefinition{
|
||||
ID: "iot_query", Name: "iot_query", DisplayName: "IoT Device Query",
|
||||
Description: "Query smart home device status. Device status is typically auto-injected; use this only when status is stale.",
|
||||
Category: "iot", Complexity: sdk.ComplexitySimple,
|
||||
Parameters: map[string]interface{}{
|
||||
"type": "object",
|
||||
"properties": map[string]interface{}{"device_id": map[string]interface{}{"type": "string"}},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (t *IoTQueryTool) Validate(args map[string]interface{}) error { return nil }
|
||||
|
||||
func (t *IoTQueryTool) Execute(ctx context.Context, args map[string]interface{}) (*sdk.ToolResult, error) {
|
||||
if t.iotClient == nil {
|
||||
return &sdk.ToolResult{ToolName: "iot_query", Success: false, Error: "IoT client not configured"}, nil
|
||||
}
|
||||
|
||||
deviceID, _ := args["device_id"].(string)
|
||||
if deviceID != "" {
|
||||
dev, err := t.iotClient.GetDevice(ctx, deviceID)
|
||||
if err != nil {
|
||||
return &sdk.ToolResult{ToolName: "iot_query", Success: false, Error: err.Error()}, nil
|
||||
}
|
||||
return &sdk.ToolResult{ToolName: "iot_query", Success: true, Output: formatDevice(dev)}, nil
|
||||
}
|
||||
|
||||
devices, err := t.iotClient.GetAllDevices(ctx)
|
||||
if err != nil {
|
||||
return &sdk.ToolResult{ToolName: "iot_query", Success: false, Error: err.Error()}, nil
|
||||
}
|
||||
if len(devices) == 0 {
|
||||
return &sdk.ToolResult{ToolName: "iot_query", Success: true, Output: "No devices found"}, nil
|
||||
}
|
||||
var out string
|
||||
for _, d := range devices {
|
||||
out += formatDeviceLine(&d) + "\n"
|
||||
}
|
||||
return &sdk.ToolResult{ToolName: "iot_query", Success: true, Output: out}, nil
|
||||
}
|
||||
|
||||
func formatDevice(d *sdk.IoTDeviceState) string {
|
||||
emoji := deviceEmoji(d.Type)
|
||||
return fmt.Sprintf("%s %s (%s)\n Status: %s\n ID: %s", emoji, d.Name, d.Type, d.Status, d.ID)
|
||||
}
|
||||
|
||||
func formatDeviceLine(d *sdk.IoTDeviceState) string {
|
||||
emoji := deviceEmoji(d.Type)
|
||||
switch d.Type {
|
||||
case "light":
|
||||
return fmt.Sprintf("%s %s: %s (brightness: %d, color: %s)", emoji, d.Name, d.Status, d.Brightness, d.Color)
|
||||
case "ac":
|
||||
return fmt.Sprintf("%s %s: %s (mode: %s, temp: %.1fC)", emoji, d.Name, d.Status, d.Mode, d.Temperature)
|
||||
case "curtain":
|
||||
return fmt.Sprintf("%s %s: %s (position: %d%%)", emoji, d.Name, d.Status, d.Position)
|
||||
case "sensor":
|
||||
return fmt.Sprintf("%s %s: %.1f%s", emoji, d.Name, d.Value, d.Unit)
|
||||
case "lock":
|
||||
return fmt.Sprintf("%s %s: %s (battery: %d%%)", emoji, d.Name, d.Status, d.Battery)
|
||||
default:
|
||||
return fmt.Sprintf("%s %s: %s", emoji, d.Name, d.Status)
|
||||
}
|
||||
}
|
||||
|
||||
func deviceEmoji(t string) string {
|
||||
switch t {
|
||||
case "light":
|
||||
return "\U0001F4A1"
|
||||
case "ac":
|
||||
return "❄️"
|
||||
case "curtain":
|
||||
return "\U0001F3E0"
|
||||
case "sensor":
|
||||
return "\U0001F4CA"
|
||||
case "lock":
|
||||
return "\U0001F512"
|
||||
default:
|
||||
return "\U0001F4E6"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "iot_query",
|
||||
"displayName": "IoT Device Query",
|
||||
"version": "1.0.0",
|
||||
"minCyreneVersion": "1.0.0",
|
||||
"author": { "name": "Cyrene Team" },
|
||||
"description": "Query smart home device status (single device or all devices)",
|
||||
"license": "MIT",
|
||||
"keywords": ["iot", "query", "device", "status"],
|
||||
"category": "iot",
|
||||
"permissions": ["iot:read"]
|
||||
}
|
||||
Reference in New Issue
Block a user